<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://www.josephguadagno.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.josephguadagno.net/" rel="alternate" type="text/html" /><updated>2026-03-23T14:29:43-07:00</updated><id>https://www.josephguadagno.net/feed.xml</id><title type="html">JosephGuadagno.net</title><subtitle>Open for Work, International Public Speaker, Community Evangelist, and Family Man</subtitle><author><name>Joseph Guadagno</name></author><entry><title type="html">Squad: Tips, Tricks, and Lessons Learned</title><link href="https://www.josephguadagno.net/2026/03/21/squad-tips-tricks-and-lessons-learned" rel="alternate" type="text/html" title="Squad: Tips, Tricks, and Lessons Learned" /><published>2026-03-21T11:00:00-07:00</published><updated>2026-03-21T11:00:00-07:00</updated><id>https://www.josephguadagno.net/2026/03/21/squad-tips-tricks-and-lessons-learned</id><content type="html" xml:base="https://www.josephguadagno.net/2026/03/21/squad-tips-tricks-and-lessons-learned"><![CDATA[<p>I’ve been using Squad for about a week now, and I’ve learned a lot about how to get the most out of it. In this post, I’ll share some tips, tricks, and lessons learned that can help you use Squad more effectively.  If you haven’t heard of Squad before, its a new project from <a href="https://github.com/bradygaster" target="_blank">Brady Gaster</a> that gives you an AI development team through GitHub Copilot. Describe what you’re building. Get a team of specialists — frontend, backend, tester, lead — that live in your repo as files. They persist across sessions, learn your codebase, share decisions, and get better the more you use them.</p>

<p>In a nutshell, Squad is like having a team of AI assistants that can help you with all aspects of software development, from planning and design to implementation and testing. It’s a powerful tool that can help you be more productive and efficient, but it does require some learning and experimentation to get the most out of it.</p>

<ul>
  <li>Squad <a href="https://github.com/bradygaster/squad/" target="_blank">repository</a></li>
  <li>Squad <a href="https://bradygaster.github.io/squad/docs/" target="_blank">documentation</a></li>
</ul>

<p class="notice--info"><strong><em>Note</em></strong>: This blog post was written on an early <em>alpha</em> version of Squad, so some of the details may change as the product evolves. However, I believe the general principles and tips will still be relevant.</p>

<p>I’ve used Squad on two projects so far: my open-source project Joseph Guadagno <a href="https://github.com/jguadagno/jjgnet-broadcast/" target="_blank">Broadcasting</a> and a new project I was starting to build.  This gave me a two different contexts to learn how to use Squad effectively. A “mature” codebase with a lot of existing code and patterns to learn from, and a “greenfield” codebase where I could define the architecture and patterns from scratch.</p>

<p>For the Broadcasting project, when I initialized the Squad, it created the <a href="https://github.com/jguadagno/jjgnet-broadcast/blob/main/.squad/team.md" target="_blank">squad</a>  with the names of <a href="https://en.wikipedia.org/wiki/The_Matrix#Cast" target="_blank">cast</a> of the <a href="https://en.wikipedia.org/wiki/The_Matrix" target="_blank">Matrix</a> movies. You can customize your <a href="https://bradygaster.github.io/squad/docs/features/team-setup/" target="_blank">team</a> and their roles or chose to have them randomly generated.  I think there are about 11 preconfigured teams to choose from.</p>

<p>On to the tips, tricks, and lessons learned!</p>

<h2 id="the-relationship">The Relationship</h2>

<p>First off, I want to talk about the relationship between you and your Squad.  Your Squad is like a team of assistants that are there to help you, but they are not there to replace you.  They are there to augment your abilities and help you be more productive, but they still need your guidance and oversight. Like any team, you need to establish a good working relationship with your Squad.  This means communicating clearly, setting expectations, and providing feedback.  The better the relationship you have with your Squad, the more effective they will be.  This also means, that either you or your Squad can take the lead on different tasks.  Sometimes you may want to take the lead and have your Squad follow your instructions, and other times you may want to let your Squad take the lead and provide guidance as needed.  The key is to be flexible and adapt to the situation.</p>

<p>It’s also important to remember that your Squad is not perfect.  They will make mistakes, and they will not always understand your intentions.  This is why it’s important to provide feedback and correct them when they make mistakes.  The more feedback you provide, the better your Squad will become.  It’s a learning process for both you and your Squad, and it’s important to be patient and persistent as you work together to build a strong relationship.  The Squad will learn from you and document your requests and preferences in the code base, so the more you use it and provide feedback, the better it will become at understanding your needs and preferences.  You can also customize your Squad’s behavior and preferences by editing the files in the <code class="language-plaintext highlighter-rouge">.github/agents/</code> directory of your repo.  This is a powerful way to tailor your Squad to your specific needs and preferences. I “cheated” a took a head start and used the <a href="https://github.com/bradygaster/squad/blob/main/.github/agents/squad.agent.md" target="_blank">squad.agent.md</a> from the Squad repository as a template for my Squad’s files, but you can customize them as much as you want.  The key is to find a balance between providing guidance and allowing your Squad to learn and adapt on its own.</p>

<p>It’s also important to remember that you need to ease into the relationship with your Squad.  Go all in too fast and the Squad might tell you to take a minute.</p>

<blockquote>
  <p>Sorry, you’ve hit a rate limit that restricts the number of Copilot model requests you can make within a specific time period. Please try again in 1 minute. Please review our Terms of Service (https://docs.github.com/site-policy/github-terms/github-terms-of-service). (Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)</p>
</blockquote>

<p>This is not really the Squad telling you to take a break, but you getting rate limited by GitHub Copilot.  This is a good reminder that while Squad is a powerful tool, it does have limitations and it’s important to be mindful of those limitations as you work with your Squad.</p>

<h2 id="set-boundaries-and-expectations">Set Boundaries and Expectations</h2>

<p>Associated with the relationship is the importance of setting boundaries and expectations with your Squad.  This means being clear about what you want your Squad to do and what you don’t want them to do.  For example, you may want to set boundaries around certain types of tasks that you want to handle yourself, or certain areas of the codebase that you don’t want your Squad to touch.  Setting these boundaries and expectations can help prevent misunderstandings and ensure that your Squad is working in a way that is aligned with your goals and preferences.  I created a label in my repo called <a href="https://github.com/jguadagno/jjgnet-broadcast/issues?q=state%3Aopen%20label%3Asquad%3AJoe" target="_blank">squad:Joe</a> that I use to track tasks that I want to handle myself, and I told the Squad to never work on issues with that label unless I explicitly ask them to.  The prompt I used for that is:</p>

<blockquote>
  <p>Team, I created a label called <code class="language-plaintext highlighter-rouge">squad:Joe</code> that I will use to track tasks that I, the human, want to handle myself. Please do not work on any issues with that label 
unless I explicitly ask you to assist with them.</p>
</blockquote>

<p>This is a simple way to set a boundary with your Squad and ensure that they are not working on tasks that you want to handle yourself.  You can use similar prompts to set other boundaries and expectations with your Squad as needed.</p>

<h3 id="expectations">Expectations</h3>

<h4 id="pull-requests-and-branches">Pull Requests and Branches</h4>

<p>Initially, the Squad was committing directly to the main branch of my repo, which is not ideal.  I wanted them to create a branch and a pull request for any changes they wanted to make, so I set that expectation with the following prompt:</p>

<blockquote>
  <p>Team, for any changes to the codebase, there must be an issue created in the GitHub repo, and any code changes must be made in a branch with an associated pull request. Pull requests must also be tagged with the issue number. Please do not commit directly to the main branch.</p>
</blockquote>

<p>This allows me to review any changes that the Squad wants to make before they are merged into the main branch, and it also allows me to provide feedback and guidance on those changes as needed.  Setting this expectation has helped me maintain control over the codebase while still allowing the Squad to contribute and make changes as needed. After the team get’s the hang of your repository and your preferences, you can start to loosen the boundaries and allow them to take more initiative and make changes on their own.  The key is to find a balance that works for you and your Squad, and to be flexible and adapt as needed.</p>

<p>I’ve also asked Neo, my “architect” team member, to review the pull requests created by the Squad and provide feedback on the architecture and design of the changes.  This has been helpful in ensuring that the changes made by the Squad are aligned with the overall architecture and design of the project.</p>

<blockquote>
  <p>Neo, as the architect of the team, please review any pull requests created by the Squad and provide feedback on the architecture and design of the changes.</p>
</blockquote>

<p>Neo, has caught a few things that the Squad has done that I may have missed, and has provided helpful feedback on how to improve the architecture and design of the changes.  This has been a valuable part of the process and has helped ensure that the changes made by the Squad are of high quality and aligned with the overall goals of the project.</p>

<p><img src="/assets/images/posts/2026/squad-tips-tricks-and-lessons-learned/neo-code-review.jpeg" alt="Neo's Code Review" /></p>

<p>Sample <a href="https://github.com/jguadagno/jjgnet-broadcast/pull/553#pullrequestreview-3984571759" target="_blank">Code Review</a> from Neo on a pull request created by the Squad.</p>

<h4 id="testing">Testing</h4>

<p>My tester, Tank, is good at creating tests, however, he would create them and commit them without validating that they were correct or even running them.  This is not ideal, so I set the expectation with the following prompt:</p>

<blockquote>
  <p>Tank, going forward, before you commit any tests to the codebase, please run them locally to ensure that they are correct and passing. This will help ensure that the tests you create are effective and reliable, and it will also help prevent any issues with failing tests being committed to the codebase.</p>
</blockquote>

<p>This has helped improve the quality of the tests created by Tank and has also helped prevent any issues with failing tests being committed to the codebase.  It’s important to set expectations around testing and quality assurance to ensure that the changes made by the Squad are of high quality and do not introduce any issues or bugs into the codebase. It also prevented a lot of wasting of time with failed builds and deployments.</p>

<h2 id="helpful-commands">Helpful Commands</h2>

<h3 id="initial-setup">Initial Setup</h3>

<p>After setting up your Squad, I recommend you run the following prompt to get your Squad to learn about your codebase and document it for future reference.  This will help your Squad understand the architecture and design of your codebase, and it will also help them learn from the existing code and patterns in your codebase.</p>

<blockquote>
  <p>Team, to help you get up to speed with the codebase, please take some time to explore the codebase and learn about its architecture and design. As you explore, please document your findings and any patterns or best practices you discover in the codebase. Also, make any recommendations for improving the architecture or design of the codebase as you see fit. This will help you learn from the existing code and patterns in the codebase, and it will also help you understand how to work effectively within the existing architecture and design of the project.</p>
</blockquote>

<p class="notice--info"><strong><em>Additional Tip</em></strong>: This is a good prompt to run periodically as your Squad learns and evolves, to help them continue to learn and adapt to the codebase as it changes over time.</p>

<p>Also, this prompt, or more so “<em>make any recommendations for improving the architecture or design of the codebase as you see fit</em>”, is not for faint of heart, or those who are not open to criticism.  The initial list of recommendations I got back from the Squad was pretty overwhelming, somewhere around 150-200 recommendations. As I reviewed them, I found that mosts of them were important, helpful, and valid.  Many of which were things I did not think about like, security, setting up a better CI/CD pipeline, duplicate code, better error handling, better logging, and the list goes on.</p>

<p>You can view the initial recommendations I got back from the Squad in the <a href="https://github.com/jguadagno/jjgnet-broadcast/issues?q=is%3Aissue%20created%3A%3E2025-02-19" target="_blank">issues</a> of my repo.  I recommend you review the recommendations and prioritize them based on their importance and impact on the project.  This can help you identify the most critical issues and improvements that need to be addressed, and it can also help you create a plan for addressing those issues and improvements over time.</p>

<p>In my case, I asked Neo to review the recommendations and help me prioritize them based on their importance and impact on the project.  This has been helpful in identifying the most critical issues and improvements that need to be addressed, and it has also helped me create a plan for addressing those issues and improvements over time. And create sprints for the Squad to work on.</p>

<p>Prompt I used for that is:</p>

<blockquote>
  <p>Neo, as the architect of the team, please review the recommendations provided by the Squad and help me prioritize them based on their importance and impact on the project.</p>
</blockquote>

<p>After a few moments of review, Neo came back with a prioritized list of recommendations and a plan for addressing those recommendations over time. While I was not thrilled with the names of the sprints, I thought they were pretty funny and sadly accurate.</p>

<p>The first sprint was “Fire Fighting: Stop the Bleeding”, which was focused on mostly security issues and critical bugs the needed to be addressed immediately. The second was “Pipeline Integrity: Broadcasting actually works”, which was focused on improving the CI/CD pipeline and ensuring that the project could be built, tested, and deployed reliably. Ouch!</p>

<p><img src="/assets/images/posts/2026/squad-tips-tricks-and-lessons-learned/squad-sprints.png" alt="Sprint Prioritization" /></p>

<h3 id="status-updates">Status Updates</h3>

<p>Ralph, is the “coordinator” of the team.  Ralph is a built-in squad member whose job is keeping tabs on work. Like Scribe tracks decisions, Ralph tracks and drives the work queue. He’s always on the roster — not cast from a universe — and has one job: make sure the team never sits idle when there’s work to do.</p>

<p>I found that asking Ralph for regular status updates on the work queue was helpful in keeping track of what the Squad was working on and what tasks were in the queue.  I set the expectation with the following prompt:</p>

<blockquote>
  <p>Ralph, as the coordinator of the team, please provide status updates every minute on the work queue.</p>
</blockquote>

<p>This was helpful when I was working with the Squad cli, which I learned is not the preferred way to interact with the Squad. The preferred way is to interact with the Squad through the GitHub Copilot <a href="https://github.com/features/copilot/cli/" target="_blank">CLI</a>, which provides more functionality, status updates, and control over the Squad.  When I switched to using the GitHub Copilot CLI, I found that I didn’t need to ask Ralph for regular status updates as much, because the CLI provided more visibility into what the Squad was working on and what tasks were in the queue.</p>

<h3 id="other-tips">Other tips</h3>

<p>Know what is running and being used by the Squad.  I was doing something in Visual Studio Code, not related to this project, and when I was finished I closed Visual Studio Code. After closing Visual Studio Code, I got a message in the terminal that Squads was running in said that “IDE Connection lost: Visual Studio Code - Insiders closed”. I guess that the Squad was using Visual Studio Code - Insiders to build the solution.</p>

<p><img src="/assets/images/posts/2026/squad-tips-tricks-and-lessons-learned/vs-code-closed.png" alt="IDE Connection Lost" class="align-center" /></p>

<p>Be cautious about asking your Squad to do too much at once. While there is not much, that I have seen that the Squad cannot do, like any team, they can start to step over each other and get in each other’s way if they are working on too many tasks at once.  This can lead to confusion and inefficiency, so it’s important to manage the work queue and ensure that the Squad is focused on a manageable number of tasks at any given time. Also, you run the risk of getting rate limited by GitHub and the recovery time can be long and sometimes it’s challenging for the Squad to pick back up where they left off after a rate limit.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>If you have any questions about Squad or want to share your own tips and tricks, please reach out to me on <a href="https://bsky.app/profile/jguadagno.com" target="_blank">Bluesky</a> or shoot me an <a href="mailto:jguadagno@hotmail.com" target="_blank">email</a>. I’m excited to see how the community uses Squad and what we can learn from each other!</p>

<h2 id="references">References</h2>

<ul>
  <li>Squad <a href="https://github.com/bradygaster/squad/" target="_blank">repository</a></li>
  <li>Squad <a href="https://bradygaster.github.io/squad/docs/get-started/installation/" target="_blank">documentation</a></li>
  <li>GitHub Copilot <a href="https://github.com/features/copilot" target="_blank">website</a></li>
  <li>GitHub Copilot <a href="https://github.com/features/copilot/cli/" target="_blank">CLI</a></li>
  <li>Joseph Guadagno <a href="https://github.com/jguadagno/jjgnet-broadcast/" target="_blank">Broadcasting</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Squad" /><category term="AI" /><category term="Agents" /><category term="GitHub" /><category term="GitHub Copilot" /><category term="Copilot" /><summary type="html"><![CDATA[I've been using Squad for about a week now, and I've learned a lot about how to get the most out of it. In this post, I'll share some tips, tricks, and lessons learned that can help you use Squad more effectively.]]></summary></entry><entry><title type="html">JetBrains Rider Settings for Presentations</title><link href="https://www.josephguadagno.net/2026/02/16/jetbrains-rider-settings-for-presentations" rel="alternate" type="text/html" title="JetBrains Rider Settings for Presentations" /><published>2026-02-16T13:30:00-07:00</published><updated>2026-02-16T13:30:00-07:00</updated><id>https://www.josephguadagno.net/2026/02/16/jetbrains-rider-settings-for-presentations</id><content type="html" xml:base="https://www.josephguadagno.net/2026/02/16/jetbrains-rider-settings-for-presentations"><![CDATA[<p>I speak at conferences and user groups and I often use JetBrains Rider for my presentations and demos. While at CodeMash 2026, I noticed a presenter using JetBrains Rider and “missing” some of the features that I like to use when I am presenting. I walked him through some of the settings that I change when I am presenting and thought “<em>I should blog about this</em>.”</p>

<p>Like most blog posts, this is an opinionated list of settings and plugins that I use when presenting with JetBrains Rider. Your mileage may vary and you may find other settings and plugins that work better for you when presenting. The important thing is to find what works best for you when presenting and to have fun presenting with JetBrains Rider.</p>

<h2 id="plugins">Plugins</h2>

<p>There are many plugins available for JetBrains Rider, these are two that I use when presenting … in no particular order :smile:.</p>

<h3 id="window-arranger">Window Arranger</h3>

<p><a href="https://plugins.jetbrains.com/plugin/18045-window-arranger" target="_blank">Window Arranger</a></p>

<p>This plugin helps you easily switch between projects, arrange project windows, and resize them to various predefined dimensions. You can align windows horizontally or vertically, maximize, and position them to the left, right, top, or bottom, improving workspace organization and productivity.</p>

<p>When the plugin is installed and enabled, you will see a “<em>Window</em>” like icon next to the project branch, highlighted in red, like this:</p>

<p><img src="/assets/images/posts/2026/jetbrains-rider-settings-for-presentations/window-arranger-icon.png" alt="Window Arranger Icon" /></p>

<p>When you click on the icon, you will see a menu with the different options to arrange the windows.</p>

<p><img src="/assets/images/posts/2026/jetbrains-rider-settings-for-presentations/window-arranger-drop-down.png" alt="Window Arranger Drop Down Menu" /></p>

<h3 id="key-promoter-x">Key Promoter X</h3>

<p><a href="https://plugins.jetbrains.com/plugin/9792-key-promoter-x" target="_blank">Key Promoter X</a></p>

<p>The Key Promoter X helps you to learn essential shortcuts while you are working. When you use the mouse on a button inside the IDE, the Key Promoter X shows you the keyboard shortcut that you should have used instead.</p>

<p>This provides an easy way to learn how to replace tedious mouse work with keyboard keys and helps to transition to a faster, mouse free development. The Key Promoter X tool window shows you a hit-list of the mouse actions you use the most and directly provides you with the shortcut you can use instead. For buttons that don’t have a shortcut, the Key Promoter X prompts you with the possibility to directly create one.</p>

<p>Here is an example of the Key Promoter X showing a keyboard shortcut (<code class="language-plaintext highlighter-rouge">F9</code>) for the <em>Toggle Breakpoint</em> action when I click on the red dot in the gutter to set a breakpoint:</p>

<p><img src="/assets/images/posts/2026/jetbrains-rider-settings-for-presentations/key-promoter-x.jpg" alt="Key Promoter X" /></p>

<h3 id="bundled-plugins">Bundled Plugins</h3>

<p>This plugins are bundled with JetBrains Rider and I enable them when I am presenting.</p>

<h4 id="keymaps">Keymaps</h4>

<p>I use these mostly because of muscle memory, being a long time user of Visual Studio and ReSharper. Plus, as a bonus, it allows me to show the keyboard shortcuts that most of the attendees of the presentation are used to.</p>

<ul>
  <li>ReSharper Keymap</li>
  <li>Visual Studio 2022 Keymap</li>
  <li>Visual Studio Keymap</li>
</ul>

<h4 id="others">Others</h4>

<ul>
  <li>Terminal: I use the terminal a lot when presenting and this plugin allows me to have a terminal window inside of JetBrains Rider and not have to jump to the operating system terminal.</li>
  <li>Database Tools and SQL: I use this plugin when I am presenting database related topics and it allows me to connect to databases and run SQL queries inside of JetBrains Rider.</li>
</ul>

<h2 id="settings">Settings</h2>

<p>For the settings, I use the default settings for the most part, but there are a few settings that I change when I am presenting. These settings are mostly related to the appearance of the IDE and the editor.</p>

<h3 id="appearance-and-behavior">Appearance and Behavior</h3>

<p>In the Appearance section, the only “non-default” setting that I change is the <em>Presentation Mode</em> Zoom level, which I set to <code class="language-plaintext highlighter-rouge">175%</code>. This setting is used when you enter presentation mode which I will cover in more detail in the <em>Tool Windows / IDE Features &gt; Appearance</em> section below. This setting allows me to make the text and the UI elements larger when I am presenting, which makes it easier for the attendees to see what I am doing on the screen.</p>

<h4 id="appearance--behavior--presentation-assistant">Appearance &amp; Behavior &gt; Presentation Assistant</h4>

<p>There used to be a plugin called Presentation Assistant that would show the keyboard shortcuts on the screen when you used them. This plugin is no longer available because it was replaced by built-in features. These settings in the built-in Presentation Assistant help you to make the keyboard shortcuts more visible when presenting.</p>

<p>Navigate to <code class="language-plaintext highlighter-rouge">Settings &gt; Appearance &amp; Behavior &gt; Presentation Assistant</code> and review the following settings:</p>

<ul>
  <li>Check <em>Show action names and shortcuts in popups</em> to show the action names and shortcuts in popups when you use them.</li>
  <li>Assign the <em>Keymaps</em> you would like the Presentation Assistant to show when you use the keyboard shortcuts. I use the Keymaps of  <em>ReSharper Keymap</em> and  <em>Visual Studio 2022 Keymap</em> when presenting.</li>
</ul>

<p>My settings look like this:</p>

<p><img src="/assets/images/posts/2026/jetbrains-rider-settings-for-presentations/presentation-assistant-settings.png" alt="Presentation Assistant Settings" /></p>

<h3 id="editor">Editor</h3>

<h4 id="editor--general">Editor &gt; General</h4>

<p>I change the following settings in <code class="language-plaintext highlighter-rouge">Settings &gt; Editor &gt; General</code> when I am presenting:</p>

<h5 id="editor--general--mouse-control">Editor &gt; General &gt; Mouse Control</h5>

<ul>
  <li>Check the <em>Change font size with Ctrl+Mouse Wheel in:</em> and select <em>All editors</em> to allow me to quickly adjust the font size if I need to during a presentation.</li>
  <li>Check the <em>More code fragments with drag and drop</em> to allow me to easily move code around when I am presenting.</li>
</ul>

<h5 id="editor--general--soft-wraps">Editor &gt; General &gt; Soft Wraps</h5>

<p>This setting is more of a preference, but I like to check the <em>Soft-wrap these files:</em> and add <code class="language-plaintext highlighter-rouge">*.md; *.txt; *.rst; *.adoc</code> to allow me to easily read markdown files and other text files when I am presenting.</p>

<h5 id="editor--general--appearance">Editor &gt; General &gt; Appearance</h5>

<ul>
  <li>Check the <em>Show line numbers:</em> and set it to <em>Absolute</em> so I can reference line numbers when I am presenting and attendees can easily follow along.</li>
</ul>

<h5 id="editor--general--code-folding">Editor &gt; General &gt; Code Folding</h5>

<p>These are more of a preference, and depending on the type of presentation I am giving, I may check or uncheck these settings. My default settings are:</p>

<p><em>Show code folding arrows</em> is checked and set to <em>On mouse hover</em>.</p>

<p><strong>Fold by default</strong> is checked for the following:</p>

<ul>
  <li>General &gt; File header</li>
  <li>General &gt; Imports</li>
  <li>Android &gt; String references</li>
  <li>JSON &gt; Show key count in folded JSON</li>
  <li>Markdown &gt; Collapse front matter</li>
  <li>Markdown &gt; Collapse links</li>
  <li>Markdown &gt; Collapse table of contents</li>
  <li>XML &gt; HTML ‘style’ attribute</li>
  <li>XML &gt; XML Entities</li>
  <li>XML &gt; Data URIs</li>
  <li>YAML &gt; Limit folded keys and values to <code class="language-plaintext highlighter-rouge">20</code> characters</li>
  <li>.NET &gt; Preprocessor regions</li>
  <li>C/C++ &gt; Conditionally non-compiled code</li>
  <li>F# &gt; F# hash directives blocks</li>
</ul>

<h4 id="editor--font">Editor &gt; Font</h4>

<p>Now this setting has a lot of personal preferences, but the two settings that I use when presenting are:</p>

<ul>
  <li>Font: I also use a Mono spaced font when presenting and coding so I really don’t have to change this setting when I am presenting. I use <a href="https://www.jetbrains.com/lp/mono/" target="_blank">JetBrainsMono Nerd Font Mono</a>, but you can use any Mono spaced font that you like.</li>
  <li>Font size is usually set to <code class="language-plaintext highlighter-rouge">13</code> and line height is set to <code class="language-plaintext highlighter-rouge">1.0</code> when I am presenting. However, this will be adjusted based on the size of the room and the size of the screen that I am presenting on. I usually start with these settings and then adjust them as needed during the presentation.  I’ll cover this is more detail in the <em>Important Keyboard Shortcuts</em> and <em>Tool Windows / IDE Features</em> section below.</li>
</ul>

<h2 id="tool-windows--ide-features">Tool Windows / IDE Features</h2>

<h3 id="tool-windows">Tool Windows</h3>

<p>These are available via shortcuts and via the <em>View &gt; Tool Windows</em> menu. I use these tool windows when I am presenting to show different aspects of the IDE and to help me navigate through the code and the project. Some of the tool windows I use when presenting are:</p>

<ul>
  <li>Explorer: <code class="language-plaintext highlighter-rouge">CTRL+ALT+L</code> or <em>View &gt; Tool Windows &gt; Explorer</em> to show the project structure and files when I am presenting.</li>
  <li>Database: <code class="language-plaintext highlighter-rouge">CTRL+\+S</code> or <em>View &gt; Tool Windows &gt; Database</em> to show the database connections and queries when I am presenting.</li>
  <li>TODO: <code class="language-plaintext highlighter-rouge">CTRL+\+T</code> or <em>View &gt; Tool Windows &gt; TODO</em> to show the TODO items in the code when I am presenting.</li>
</ul>

<h3 id="appearance">Appearance</h3>

<p>These are available via shortcuts and via the <em>View &gt; Appearance</em> menu. I use these appearance features when I am presenting to show reduce distractions and to help me navigate through the code and the project.</p>

<p><img src="/assets/images/posts/2026/jetbrains-rider-settings-for-presentations/appearance-menu.png" alt="Appearance Menu" /></p>

<p>Some of the appearance features I use when presenting are:</p>

<h4 id="appearance--presentation-mode">Appearance &gt; Presentation Mode</h4>

<p>When I am ready to start presenting, I enter the presentation mode by going to <em>View &gt; Appearance &gt; Enter Presentation Mode</em>. This will hide all the tool windows and the menu bar and will make the editor take up the entire screen. This is a great way to focus on the code and the presentation without any distractions.</p>

<p>It’s going to be a little hard to see in the screenshot, but when you enter presentation mode, the menu bar and the tool windows are hidden and the editor takes up the entire screen. This screenshot is at a 1920x1440 resolution, so you can see that the editor is taking up the entire screen with just the code window, and there are no distractions.</p>

<p><img src="/assets/images/posts/2026/jetbrains-rider-settings-for-presentations/presentation-mode.png" alt="Presentation Mode" /></p>

<p>While in presentation mode, you can still access the tool windows and the menu bar by using the shortcuts or by moving your mouse to the top of the screen to show the menu bar and the tool windows. This allows you to quickly access the features of the IDE without having to exit presentation mode.</p>

<p><img src="/assets/images/posts/2026/jetbrains-rider-settings-for-presentations/presentation-mode-menu.png" alt="Presentation Mode with Menu Bar" /></p>

<p>You’ll also notice, that in the upper right corner of menu bar, there is a clock with the current time. This is a great way to keep track of time during a presentation and to make sure that you are staying on schedule. Plus, there is a <em>Presentation Mode</em> indicator next to the clock, so you can easily exit presentation mode.</p>

<h4 id="appearance--presentation-assistant">Appearance &gt; Presentation Assistant</h4>

<p>Choose the <em>Presentation Assistant</em> option from the <em>View &gt; Appearance</em> menu to show the keyboard shortcuts on the screen when you use them. This is a great way to show the attendees what keyboard shortcuts you are using during a presentation and to help them learn the shortcuts as well.</p>

<p><img src="/assets/images/posts/2026/jetbrains-rider-settings-for-presentations/presentation-assistant-showing.png" alt="Presentation Assistant" /></p>

<p>You can see in the screenshot above that I pressed <code class="language-plaintext highlighter-rouge">CTRL+ALT+S</code> to open the settings and the Presentation Assistant is showing the keyboard shortcut on the screen. This is a great way to show the attendees what keyboard shortcuts you are using during a presentation and to help them learn the shortcuts as well. Especially, if you forget to mention the keyboard shortcut that you are using during a presentation.</p>

<h2 id="important-keyboard-shortcuts">Important Keyboard Shortcuts</h2>

<p>You can find all the keyboard shortcuts in the <em>Settings &gt; Keymap</em> section, but here are some of the important keyboard shortcuts that I use when presenting, besides the typical <em>CUT, COPY, PASTE</em> shortcuts.</p>

<p>These shortcuts are with the Visual Studio 2022 Keymap, but you can use the shortcuts from the Keymap that you are using when presenting.</p>

<h3 id="general">General</h3>

<table>
  <tbody>
    <tr>
      <td><strong>Action</strong></td>
      <td><strong>Shortcut</strong></td>
      <td><strong>Description</strong></td>
    </tr>
    <tr>
      <td><em>Build Solution</em></td>
      <td><code class="language-plaintext highlighter-rouge">CTRL+SHIFT+B</code></td>
      <td>Build the entire solution.</td>
    </tr>
    <tr>
      <td><em>Increase Font Size</em></td>
      <td><code class="language-plaintext highlighter-rouge">CTRL+Mouse Wheel Up</code></td>
      <td>Increase the font size.</td>
    </tr>
    <tr>
      <td><em>Increase Font Size in All Editors</em></td>
      <td><code class="language-plaintext highlighter-rouge">CTRL+SHIFT+.</code></td>
      <td>Increase the font size in all editors.</td>
    </tr>
    <tr>
      <td><em>Decrease Font Size in All Editors</em></td>
      <td><code class="language-plaintext highlighter-rouge">CTRL+SHIFT+,</code></td>
      <td>Decrease the font size in all editors.</td>
    </tr>
    <tr>
      <td><em>Code Completion</em></td>
      <td><code class="language-plaintext highlighter-rouge">CTRL+SPACE</code></td>
      <td>Show code completion suggestions.</td>
    </tr>
  </tbody>
</table>

<h3 id="navigation">Navigation</h3>

<table>
  <tbody>
    <tr>
      <td><strong>Action</strong></td>
      <td><strong>Shortcut</strong></td>
      <td><strong>Description</strong></td>
    </tr>
    <tr>
      <td><em>Go to File</em></td>
      <td><code class="language-plaintext highlighter-rouge">CTRL+SHIFT+T</code></td>
      <td>Go to a file in the project.</td>
    </tr>
    <tr>
      <td><em>Search Everywhere</em></td>
      <td><code class="language-plaintext highlighter-rouge">CTRL+T</code> or <code class="language-plaintext highlighter-rouge">CTRL+,</code></td>
      <td>Search for anything in the IDE.</td>
    </tr>
    <tr>
      <td><em>Go to Line</em></td>
      <td><code class="language-plaintext highlighter-rouge">CTRL+G</code></td>
      <td>Go to a specific line in the editor.</td>
    </tr>
  </tbody>
</table>

<h2 id="wrap-up">Wrap Up</h2>

<p>As I mentioned at the beginning of this post, these are just some of the settings and plugins that I use when presenting with JetBrains Rider. There are many other settings and plugins that you can use when presenting with JetBrains Rider, so I encourage you to explore the settings and plugins and find what works best for you when presenting.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://www.jetbrains.com/rider/" target="_blank">JetBrains Rider</a></li>
  <li><a href="https://plugins.jetbrains.com/rider" target="_blank">JetBrains Rider Plugins</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Rider" /><category term="Settings" /><category term="Presentation" /><category term="Presentations" /><category term="presenting" /><category term="JetBrains" /><category term="Plugin" /><summary type="html"><![CDATA[I have been using JetBrains Rider for a while now and I have been really happy with it. I have also been using it for presentations and demos. I thought I would share how I set it up for presentations.]]></summary></entry><entry><title type="html">Introducing MoreSpeakers.com</title><link href="https://www.josephguadagno.net/2026/01/30/introducing-morespeakers-com" rel="alternate" type="text/html" title="Introducing MoreSpeakers.com" /><published>2026-01-30T11:18:00-07:00</published><updated>2026-01-30T11:18:00-07:00</updated><id>https://www.josephguadagno.net/2026/01/30/introducing-morespeakers-com</id><content type="html" xml:base="https://www.josephguadagno.net/2026/01/30/introducing-morespeakers-com"><![CDATA[<p>For the last two months I’ve been working on an exciting new project called <a href="https://morespeakers.com" target="_blank" rel="noopener">MoreSpeakers.com</a>. This platform is dedicated to bridging the gap between aspiring and experienced speakers in the technology community by creating meaningful mentorship relationships that foster growth, confidence, and knowledge sharing.  The project is a collaboration between myself, <a href="https://www.woodruff.dev/" target="_blank" rel="noopener">Chris Woodruff</a>, <a href="https://blog.peterritchie.com/about" target="_blank" rel="noopener">Peter Ritchie</a>, and <a href="https://github.com/curajorge" target="_blank" rel="noopener">Jorge Cura</a>. The idea was born, like most great ideas, from a conference conversation between Chris Woodruff and Scott Hunter during a <em>TibiTab</em>, if you know you know :smile:, about the challenges of getting started in public speaking.</p>

<p>Chris started by building a working prototype using Claude Code to see what it can do. After seeing the potential, I joined the project to help turn the prototype into a full-fledged platform. Over the next two months, we worked to design, develop, and launch MoreSpeakers.com.</p>

<p><img src="/assets/images/posts/2026/introducing-morespeakers-com/hero-illustration.png" alt="MoreSpeakers.com Hero Image" class="align-center" /></p>

<h2 id="key-features">Key Features</h2>

<p>The platform’s main goal is to connect aspiring speakers with experienced mentors who can guide them through the process of becoming confident public speakers. Once a user signs up, they can create a profile highlighting their speaking interests (expertise’s) and goals. They can then browse through a list of available mentors, read their profiles, and request mentorship. Experienced speakers can also sign up to become mentors, sharing their knowledge and helping others grow.</p>

<h3 id="how-it-works">How It Works</h3>

<p>First step is to <strong>Sign Up</strong>. Users can create an account on <a href="https://morespeakers.com/Register" target="_blank" rel="noopener">MoreSpeakers.com</a>, providing basic information about themselves and their speaking interests.  After signing up, you can browse through the list of available mentors. Each mentor has a profile that includes their areas of expertise, speaking experience, and availability. When you find a mentor that aligns with your interests and goals, you can send them a mentorship request. Once a mentor accepts your request, you can start scheduling sessions and working together to improve your speaking skills.</p>

<p>Next step is to navigate to the <strong>Browse Speakers</strong> <a href="https://morespeakers.com/speakers" target="_blank" rel="noopener">page</a>. Here, you can filter speakers based on various criteria such as expertise, availability, and location. This makes it easy to find mentors who match your specific needs.</p>

<p><img src="/assets/images/posts/2026/introducing-morespeakers-com/speakers-search.png" alt="MoreSpeakers.com Browse Speakers" class="align-center" /></p>

<p>Once you’ve found a mentor that interests you, click on their profile to learn more about their background, experience, and areas of expertise. This information will help you determine if they are the right fit for your mentorship needs.</p>

<p><img src="/assets/images/posts/2026/introducing-morespeakers-com/speakers-search-results.png" alt="MoreSpeakers.com Speaker Search Results" class="align-center" /></p>

<p>In the search results, you can see a list of speakers that match your criteria. Each entry includes the speaker’s name, areas of expertise, and a brief bio. You can click on a speaker’s name to view their full profile.</p>

<p><img src="/assets/images/posts/2026/introducing-morespeakers-com/speaker-profile.png" alt="MoreSpeakers.com Speaker Profile" class="align-center" /></p>

<p>The speaker profile provides detailed information about the speaker, including their biography, goals and objectives, areas of expertise’s, links to their social media profiles, their <a href="https://sessionize.com/" target="_blank" rel="noopener">Sessionize</a> profile (if they provided one), and more. This information will help you decide if this mentor is the right fit for you.</p>

<p>If the speaker looks like a good match, you can send them a <strong>Mentorship Request</strong> directly from their profile page by clicking on <em>Request Mentorship</em>.</p>

<h4 id="sending-a-mentorship-request">Sending a Mentorship Request</h4>

<p>After finding a mentor, you can send them a <strong>Mentorship Request</strong>.</p>

<p><img src="/assets/images/posts/2026/introducing-morespeakers-com/mentorship-request.png" alt="MoreSpeakers.com Mentorship Request" class="align-center" /></p>

<p>This request includes the expertise’s you are interested in and a brief message explaining why you are interested in working with them and what you hope to achieve through the mentorship. Once the mentor accepts your request, you can start scheduling sessions and working together to improve your speaking skills.</p>

<h2 id="the-technology-behind-morespeakerscom">The Technology Behind MoreSpeakers.com</h2>

<p>You can read all about the technology behind MoreSpeakers.com in my <a href="/2026/01/30/the-technology-behind-morespeakers-com" target="_blank" rel="noopener">detailed article</a>.</p>

<p>If you want to help out with the project, the repository is available on <a href="https://github.com/cwoodruff/morespeakers-com" target="_blank" rel="noopener">GitHub</a>. Find a bug, submit an issue and/or a pull request. We are open to <a href="https://github.com/cwoodruff/morespeakers-com/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener">contributions</a>, whether it’s fixing bugs, adding new features, or improving documentation.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>That’s it! Now you can start your journey to becoming a confident public speaker with the help of experienced mentors on MoreSpeakers.com. We are excited to see how this platform will help aspiring speakers grow and thrive in the technology community. If you’re interested in becoming a mentor or mentee, head over to <a href="https://morespeakers.com" target="_blank" rel="noopener">MoreSpeakers.com</a> and sign up today!</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://morespeakers.com" target="_blank" rel="noopener">MoreSpeakers.com</a></li>
  <li><a href="https://github.com/cwoodruff/morespeakers-com" target="_blank" rel="noopener">GitHub Repository</a></li>
  <li><a href="https://getbootstrap.com" target="_blank" rel="noopener">Bootstrap 5</a></li>
  <li><a href="https://dotnet.microsoft.com/en-us/apps/aspnet" target="_blank" rel="noopener">ASP.NET Core</a></li>
  <li><a href="https://learn.microsoft.com/en-us/ef/core/" target="_blank" rel="noopener">Entity Framework Core</a></li>
  <li><a href="https://www.aspire.dev" target="_blank" rel="noopener">Aspire</a></li>
  <li><a href="https://htmx.org" target="_blank">HTMX</a></li>
  <li><a href="https://aspnet-htmx.com/" target="_blank" rel="noopener">ASP.NET Core Reimagined with HTMX book</a></li>
  <li><a href="https://www.jetbrains.com/rider/" target="_blank" rel="noopener">JetBrains Rider</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET" /><category term="dotnet" /><category term="Development" /><category term="csharp" /><category term="Community" /><category term="Bootstrap" /><category term="ASP.NET" /><category term="Microsoft Azure" /><category term="Azure Functions" /><category term="Aspire" /><summary type="html"><![CDATA[MoreSpeakers.com is a new platform dedicated to bridging the gap between aspiring and experienced speakers in the technology community by creating meaningful mentorship relationships that foster growth, confidence, and knowledge sharing.]]></summary></entry><entry><title type="html">The Technology Behind MoreSpeakers.com</title><link href="https://www.josephguadagno.net/2026/01/30/the-technology-behind-morespeakers-com" rel="alternate" type="text/html" title="The Technology Behind MoreSpeakers.com" /><published>2026-01-30T11:18:00-07:00</published><updated>2026-01-30T11:18:00-07:00</updated><id>https://www.josephguadagno.net/2026/01/30/the-technology-behind-morespeakers-com</id><content type="html" xml:base="https://www.josephguadagno.net/2026/01/30/the-technology-behind-morespeakers-com"><![CDATA[<p>In my previous <a href="/2026/01/30/introducing-morespeakers-com" target="_blank" rel="noopener">post</a> , I introduced <a href="https://morespeakers.com" target="_blank" rel="noopener">MoreSpeakers.com</a>, a platform dedicated to connecting aspiring speakers with experienced mentors in the technology community. In this post, I want to share some insights into the technology stack and development process behind MoreSpeakers.com.</p>

<h2 id="technology-used">Technology Used</h2>

<p>MoreSpeakers.com is built using a combination of modern web technologies to ensure an user-friendly experience.</p>

<h3 id="development-stack">Development Stack</h3>

<p>Tools and frameworks used in the development of MoreSpeakers.com include.</p>

<h4 id="tools">Tools</h4>

<ul>
  <li>JetBrains <a href="https://www.jetbrains.com/rider/" target="_blank" rel="noopener">Rider</a>: The primary IDE used for development, providing a robust environment for C# and web development.</li>
  <li><a href="https://azure.microsoft.com/en-us/features/storage-explorer/" target="_blank" rel="noopener">Azure Storage Explorer</a>: Used for managing Azure Storage resources.</li>
  <li><a href="https://github.com" target="_blank" rel="noopener">GitHub</a>: For version control and collaboration among the development team.</li>
  <li>Microsoft <a href="https://azure.microsoft.com" target="_blank" rel="noopener">Azure</a>: Hosting and deployment of the application.</li>
  <li><a href="https://www.aspire.dev" target="_blank" rel="noopener">Aspire</a>: Used for to help manage dependencies and ensure an easier on boarding for new engineers.</li>
</ul>

<h4 id="frameworks-and-libraries">Frameworks and Libraries</h4>

<ul>
  <li><strong>Frontend</strong>
    <ul>
      <li>The frontend is built using <a href="https://getbootstrap.com" target="_blank" rel="noopener">Bootstrap 5</a> for responsive design and a clean user interface.</li>
      <li><a href="https://htmx.org" target="_blank" rel="noopener">HTMX</a> is used to enhance interactivity and provide a seamless user experience without the need for heavy JavaScript frameworks.</li>
    </ul>
  </li>
  <li><strong>Backend</strong>
    <ul>
      <li>The backend is developed using <a href="https://dotnet.microsoft.com/en-us/apps/aspnet" target="_blank" rel="noopener">ASP.NET Core</a>, providing a robust and scalable foundation for the application.</li>
      <li><a href="https://learn.microsoft.com/en-us/ef/core/" target="_blank" rel="noopener">Entity Framework Core</a> is used for data access and management, allowing for efficient interaction with the database.</li>
      <li><a href="https://www.microsoft.com/en-us/sql-server" target="_blank" rel="noopener">SQL Server</a> serves as the primary database for storing user profiles, mentorship requests, and other application data.</li>
    </ul>
  </li>
</ul>

<p>You can read more about the <a href="https://github.com/cwoodruff/morespeakers-com/blob/main/docs/developer-startup.md" target="_blank" rel="noopener">architecture</a> and <a href="https://github.com/cwoodruff/morespeakers-com/blob/main/docs/developer-startup.md" target="_blank" rel="noopener">developer</a> start up of MoreSpeakers.com in the GitHub repository.</p>

<h3 id="ai-and-vibe-coding">AI and Vibe Coding</h3>

<p>AI and “<em>Vibe Coding</em>” is all the rage these days. As I mentioned in the previous <a href="/2026/01/30/introducing-morespeakers-com" target="_blank" rel="noopener">post</a>, this project was prototyped with <a href="https://claude.com/product/claude-code" target="_blank" rel="noopener">Claude Code</a>. Now while the application compiled and ran, it was a mess. It took some time to get the project in a maintainable state.</p>

<p>Since then, we have continued to use AI tools to help with various aspects of the development process. I primarily used JetBrains <a href="https://www.jetbrains.com/junie/" target="_blank" rel="noopener">Junie</a> to help me with code generation, unit test creation, refactoring, and documentation. We’ve also used <a href="https://docs.github.com/en/copilot" target="_blank" rel="noopener">GitHub Copilot</a> to help with code reviews of pull requests.  We’ve used <a href="https://chat.openai.com" target="_blank" rel="noopener">ChatGPT</a> to help with the hero images and generating a lists of features to include in the platform. While AI has been a helpful tool throughout the development process, it is important to note that you still need skilled developers to “double-check” the work guide the process and ensure the quality of the final product.</p>

<h4 id="ai-instructions">AI Instructions</h4>

<p>We used the following instructions when using AI tools to help with the development of MoreSpeakers.com.</p>

<h5 id="claude-code-instructions">Claude Code Instructions</h5>

<p><a href="https://claude.com/product/claude-code" target="_blank" rel="noopener">Claude Code</a> allows you to create custom instructions to guide Claude in generating code and assisting with development tasks.</p>

<p>Our instructions are organized into two main categories: Commands and Skills. For overall instructions for Claude Code, we have:</p>

<ul>
  <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/CLAUDE.md" target="_blank" rel="noopener">Claude.md</a>: Instructions for using Claude to generate code snippets and assist with development tasks.</li>
</ul>

<h6 id="claude-code-commands">Claude Code Commands</h6>

<ul>
  <li>Claude <a href="https://github.com/cwoodruff/morespeakers-com/tree/main/.claude/commands" target="_blank" rel="noopener">Commands</a>: A collection of custom commands to streamline the use of Claude in the development process.
    <ul>
      <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.claude/commands/db-change.md" target="_blank" rel="noopener">db-change.md</a>: Instructions for making database schema changes using Claude.</li>
      <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.claude/commands/docs.md" target="_blank" rel="noopener">docs.md</a>: Instructions for generating documentation using Claude.</li>
      <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.claude/commands/feature.md" target="_blank" rel="noopener">feature.md</a>: Instructions for adding new features using Claude.</li>
      <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.claude/commands/test.md" target="_blank" rel="noopener">test.md</a>: Instructions for creating and running tests using Claude.</li>
    </ul>
  </li>
</ul>

<h6 id="claude-code-skills">Claude Code Skills</h6>

<ul>
  <li>Claude <a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.claude/skills" target="_blank" rel="noopener">Skills</a>: A set of predefined skills to enhance the capabilities of Claude in the development process.
    <ul>
      <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.claude/skills/dotnet-feature/SKILL.md" target="_blank" rel="noopener">dotnet-feature</a>: A skill for generating .NET features using Claude.</li>
      <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.claude/skills/qa-engineer/SKILL.md" target="_blank" rel="noopener">qa-engineer</a>: A skill for performing quality assurance tasks using Claude.</li>
      <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.claude/skills/sql-schema/SKILL.md" target="_blank" rel="noopener">sql-schema</a>: A skill for managing SQL schemas using Claude.</li>
    </ul>
  </li>
</ul>

<h5 id="github-copilot-instructions">GitHub Copilot Instructions</h5>

<p>GitHub Copilot allows you to create customize how GitHub Copilot responds to your prompts by creating custom instructions.</p>

<p>According to the <a href="https://docs.github.com/en/copilot/concepts/prompting/response-customization" target="_blank" rel="noopener">documentation</a>, you can create custom instructions to guide GitHub Copilot’s responses.</p>

<blockquote>
  <p>GitHub Copilot can provide responses that are tailored to your personal preferences, the way your team works, the tools you use, or the specifics of your project, if you provide it with enough context to do so. Instead of repeatedly adding this contextual detail to your prompts, you can create custom instructions that automatically add this information for you. The additional information is not displayed, but is available to Copilot to allow it to generate higher quality responses.</p>
</blockquote>

<p>We build a couple of task based agents/instructions to help with various development tasks. Here are the instructions we used for MoreSpeakers.com:</p>

<h6 id="github-agents">GitHub Agents</h6>

<ul>
  <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.github/agents/agents.md" target="_blank" rel="noopener">Agents.md</a>: Instructions for using GitHub Copilot Agents to automate development tasks.</li>
  <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.github/agents/api-agent.md" target="_blank" rel="noopener">api-agent.md</a>: An agent for managing API-related tasks using GitHub Copilot.</li>
  <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.github/agents/dev-deploy-agent.md" target="_blank" rel="noopener">dev-deploy-agent.md</a>: An agent for handling development and deployment tasks using GitHub Copilot.</li>
  <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.github/agents/docs-agent.md" target="_blank" rel="noopener">docs-agent.md</a>: An agent for managing documentation tasks using GitHub Copilot.</li>
  <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.github/agents/lint-agent.md" target="_blank" rel="noopener">lint-agent.md</a>: An agent for performing linting tasks using GitHub Copilot.</li>
  <li><a href="https://github.com/cwoodruff/morespeakers-com/blob/main/.github/agents/test-agent.md" target="_blank" rel="noopener">test-agent.md</a>: An agent for managing testing tasks using GitHub Copilot.</li>
</ul>

<h5 id="jetbrains-junie-instructions">JetBrains Junie Instructions</h5>

<p><a href="https://www.jetbrains.com/junie/" target="_blank" rel="noopener">JetBrains Junie</a> allows you to create custom instructions to guide Junie in generating code and assisting with development tasks. Instructions for customizing Junie can be found in the <a href="https://www.jetbrains.com/help/junie/customize-guidelines.html" target="_blank" rel="noopener">documentation</a>.</p>

<p>Our instructions for Junie are documented in the file <a href="https://github.com/cwoodruff/morespeakers-com/blob/main/src/.junie/guidelines.md" target="_blank" rel="noopener">guidelines.md</a>.</p>

<h3 id="want-to-contribute">Want to Contribute?</h3>

<p>If you want to help out with the project, the repository is available on <a href="https://github.com/cwoodruff/morespeakers-com" target="_blank" rel="noopener">GitHub</a>. Find a bug, submit an issue and/or a pull request. We are open to <a href="https://github.com/cwoodruff/morespeakers-com/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener">contributions</a>, whether it’s fixing bugs, adding new features, or improving documentation.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>As you can see, MoreSpeakers.com is built using a combination of modern web technologies and AI tools to provide a seamless user experience. The development process has been greatly enhanced by the use of AI, allowing us to focus on building a platform that truly meets the needs of aspiring speakers and experienced mentors in the technology community.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://morespeakers.com" target="_blank" rel="noopener">MoreSpeakers.com</a></li>
  <li><a href="https://github.com/cwoodruff/morespeakers-com" target="_blank" rel="noopener">GitHub Repository</a></li>
  <li><a href="https://getbootstrap.com" target="_blank" rel="noopener">Bootstrap 5</a></li>
  <li><a href="https://dotnet.microsoft.com/en-us/apps/aspnet" target="_blank" rel="noopener">ASP.NET Core</a></li>
  <li><a href="https://learn.microsoft.com/en-us/ef/core/" target="_blank" rel="noopener">Entity Framework Core</a></li>
  <li><a href="https://www.aspire.dev" target="_blank" rel="noopener">Aspire</a></li>
  <li><a href="https://htmx.org" target="_blank">HTMX</a></li>
  <li><a href="https://aspnet-htmx.com/" target="_blank" rel="noopener">ASP.NET Core Reimagined with HTMX book</a></li>
  <li><a href="https://www.jetbrains.com/rider/" target="_blank" rel="noopener">JetBrains Rider</a></li>
  <li><a href="https://claude.com/product/claude-code" target="_blank" rel="noopener">Claude Code</a></li>
  <li><a href="https://docs.github.com/en/copilot" target="_blank" rel="noopener">GitHub Copilot</a></li>
  <li><a href="https://www.jetbrains.com/junie/" target="_blank" rel="noopener">JetBrains Junie</a></li>
  <li><a href="https://chat.openai.com" target="_blank" rel="noopener">ChatGPT</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET" /><category term="dotnet" /><category term="Development" /><category term="csharp" /><category term="Community" /><category term="Bootstrap" /><category term="ASP.NET" /><category term="Microsoft Azure" /><category term="Azure Functions" /><category term="Aspire" /><summary type="html"><![CDATA[In this post, we delve into the technology stack and development process behind MoreSpeakers.com, a platform dedicated to connecting aspiring speakers with experienced mentors in the technology community.]]></summary></entry><entry><title type="html">Generate an Open Graph Profile Image with C#</title><link href="https://www.josephguadagno.net/2026/01/10/generate-an-open-graph-profile-image-with-c-sharp" rel="alternate" type="text/html" title="Generate an Open Graph Profile Image with C#" /><published>2026-01-10T06:28:00-07:00</published><updated>2026-01-10T06:28:00-07:00</updated><id>https://www.josephguadagno.net/2026/01/10/generate-an-open-graph-profile-image-with-c-sharp</id><content type="html" xml:base="https://www.josephguadagno.net/2026/01/10/generate-an-open-graph-profile-image-with-c-sharp"><![CDATA[<p>For the last few months, I’ve been working on a open-source project called <a href="https://morespeakers.com" target="_blank">MoreSpeakers</a>. It’s a platform that helps connect new speaker with experienced speakers to help them improve their public speaking skills. One of the features I wanted to implement was dynamic Open Graph profile images for each speaker. In this post, I’ll walk you through how I used C# to generate these images.</p>

<h2 id="what-is-open-graph">What is Open Graph?</h2>

<p>The <a href="https://ogp.me/" target="_blank">Open Graph protocol</a> enables any web page to become a rich object in a social graph. For instance, this is used on Facebook to allow any web page to have the same functionality as any other object on Facebook. By using Open Graph tags, you can control how your web pages are represented when shared on social media platforms, like Facebook, BlueSky, LinkedIn, etc.</p>

<p>To turn a web page into a rich object, you need to add specific Open Graph meta tags to the HTML of your page. These tags provide information about the page, such as its title (<code class="language-plaintext highlighter-rouge">og:title</code>), type (<code class="language-plaintext highlighter-rouge">og:type</code>), image (<code class="language-plaintext highlighter-rouge">og:image</code>), and description (<code class="language-plaintext highlighter-rouge">og:description</code>). The most relevant tag for this post is the <code class="language-plaintext highlighter-rouge">og:image</code> tag, which specifies the image that will be displayed when the page is shared.</p>

<p>I won’t go into detail about how to add Open Graph tags to your web pages, but if you’re interested, you can check out the <a href="https://ogp.me/" target="_blank">Open Graph protocol specification</a> for more information.</p>

<h2 id="generating-open-graph-images-with-c">Generating Open Graph Images with C#</h2>

<p>To generate images in C#, I used the SixLabors <a href="https://docs.sixlabors.com/articles/imagesharp/index.html" target="_blank">ImageSharp</a> library. ImageSharp is a powerful and flexible library for image processing in .NET. It supports a wide range of image formats and provides a rich set of features for manipulating images. I also needed to use the SixLabors <a href="https://docs.sixlabors.com/articles/fonts/index.html" target="_blank">Fonts</a> to generate the text for the images.</p>

<p>For an Open Graph profile image to look correct on social media platforms, it needs to be 1200 pixels wide by 630 pixels tall. This aspect ratio ensures that the image displays properly across various platforms without being cropped or distorted.  Let’s take a look at a sample image generated for a MoreSpeakers profile:</p>

<p><img src="/assets/images/posts/2026/generate-an-open-graph-profile-image-with-c-sharp/og-profile-sample.png" alt="Sample Open Graph Profile Image" /></p>

<h3 id="breaking-down-the-image">Breaking Down the Image</h3>

<p>As you can see from the sample image above, there are several key components that make up the Open Graph profile image:</p>

<ul>
  <li><strong>Speaker Image</strong>: This takes up the left side of the image and is half the width of the image (600 pixels wide by 630 pixels tall).</li>
  <li><strong>Background</strong>: The right side of the image has a gradient background that matches the MoreSpeakers branding. The gradient goes from <em>orange-red</em> (<code class="language-plaintext highlighter-rouge">#E95420</code>) to a <em>warm yellow</em> (<code class="language-plaintext highlighter-rouge">#F7C873</code>).</li>
  <li><strong>The MoreSpeakers Logo</strong>: The MoreSpeakers logo is placed at the top center of the image.</li>
  <li><strong>The Text</strong>
    <ul>
      <li><strong>“MoreSpeakers.com”</strong>: Below the logo, the text “MoreSpeakers.com” is displayed in a bold font.</li>
      <li><strong>“Speaker Profile”</strong>: Below “MoreSpeakers.com”, the text “Speaker Profile” is displayed in a regular font.</li>
      <li><strong>Speaker Name</strong>: Below “Speaker Profile”, the speaker’s name is displayed in a large, bold font.</li>
    </ul>
  </li>
</ul>

<p>Now, the code to generate this is broken out similarly to the breakdown above.  Let’s go through each part of the code.</p>

<h4 id="the-images">The Images</h4>

<p>First we start with creating a <code class="language-plaintext highlighter-rouge">canvas</code> to draw on:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">using</span> <span class="nn">var</span> <span class="n">image</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Image</span><span class="p">&lt;</span><span class="n">Rgba32</span><span class="p">&gt;(</span><span class="m">1200</span><span class="p">,</span> <span class="m">630</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h5 id="the-gradient-background">The Gradient Background</h5>

<p>Next, we need to create a <code class="language-plaintext highlighter-rouge">LinearGradientBrush</code> to create the gradient background:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">gradientBrush</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">LinearGradientBrush</span><span class="p">(</span>
    <span class="k">new</span> <span class="nf">PointF</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">),</span>
    <span class="k">new</span> <span class="nf">PointF</span><span class="p">(</span><span class="m">1200</span><span class="p">,</span> <span class="m">630</span><span class="p">),</span>
    <span class="n">GradientRepetitionMode</span><span class="p">.</span><span class="n">None</span><span class="p">,</span>
    <span class="k">new</span><span class="p">[]</span>
    <span class="p">{</span>
        <span class="k">new</span> <span class="nf">ColorStop</span><span class="p">(</span><span class="m">0f</span><span class="p">,</span> <span class="n">Color</span><span class="p">.</span><span class="nf">ParseHex</span><span class="p">(</span><span class="s">"#E95420"</span><span class="p">)),</span> <span class="c1">// orange-red</span>
        <span class="k">new</span> <span class="nf">ColorStop</span><span class="p">(</span><span class="m">1f</span><span class="p">,</span> <span class="n">Color</span><span class="p">.</span><span class="nf">ParseHex</span><span class="p">(</span><span class="s">"#F7C873"</span><span class="p">))</span> <span class="c1">// warm yellow</span>
    <span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now we can fill the image with the gradient:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">canvas</span><span class="p">.</span><span class="nf">Mutate</span><span class="p">(</span><span class="n">ctx</span> <span class="p">=&gt;</span> <span class="n">ctx</span><span class="p">.</span><span class="nf">Fill</span><span class="p">(</span><span class="n">gradientBrush</span><span class="p">));</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h5 id="the-speaker-image">The Speaker Image</h5>

<p>Since the speaker image can come in various sizes and aspect ratios, we need to resize and crop it to fit perfectly into the left half of the Open Graph image. Here’s how we can do that:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="n">speakerImage</span><span class="p">.</span><span class="nf">Mutate</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="nf">Resize</span><span class="p">(</span><span class="k">new</span> <span class="n">ResizeOptions</span>
<span class="p">{</span>
    <span class="n">Size</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Size</span><span class="p">(</span><span class="m">1200</span> <span class="p">/</span> <span class="m">2</span><span class="p">,</span> <span class="m">630</span><span class="p">),</span>
    <span class="n">Mode</span> <span class="p">=</span> <span class="n">ResizeMode</span><span class="p">.</span><span class="n">Crop</span>
<span class="p">}));</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice-info"><strong><em>NOTE</em></strong>: The <code class="language-plaintext highlighter-rouge">speakerImage</code> variable represents a <code class="language-plaintext highlighter-rouge">SixLabors.ImageSharp.Image</code> object that contains the speaker’s image. This image could be loaded from a file, URL, or stream.</p>

<p><code class="language-plaintext highlighter-rouge">Mutate</code> is the method used to change the image, and <code class="language-plaintext highlighter-rouge">ResizeOptions</code> allows us to specify the size and mode. We set the size to half the width of the Open Graph image (600 pixels) and the full height (630 pixels). The <code class="language-plaintext highlighter-rouge">ResizeMode.Crop</code> option ensures that the image fills the entire area without distortion, cropping any excess parts as necessary.</p>

<p>Now we can draw the speaker image onto the left side of the canvas:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">canvas</span><span class="p">.</span><span class="nf">Mutate</span><span class="p">(</span><span class="n">ctx</span> <span class="p">=&gt;</span> <span class="n">ctx</span><span class="p">.</span><span class="nf">DrawImage</span><span class="p">(</span><span class="n">speakerImage</span><span class="p">,</span> <span class="k">new</span> <span class="nf">Point</span><span class="p">(</span><span class="m">0</span><span class="p">,</span> <span class="m">0</span><span class="p">),</span> <span class="m">1f</span><span class="p">));</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h5 id="the-morespeakers-logo">The MoreSpeakers Logo</h5>

<p>Next, we need to load the MoreSpeakers logo and draw it onto the canvas. Assuming we have the logo image loaded into a variable called <code class="language-plaintext highlighter-rouge">logoImage</code>, we can position it at the top center of the right half of the Open Graph image:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">logoWidth</span> <span class="p">=</span> <span class="m">110</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">logoHeight</span> <span class="p">=</span> <span class="m">110</span><span class="p">;</span>
<span class="n">logoImage</span><span class="p">.</span><span class="nf">Mutate</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="nf">Resize</span><span class="p">(</span><span class="n">logoWidth</span><span class="p">,</span> <span class="n">logoHeight</span><span class="p">));</span>

<span class="c1">// Paste the logo in the center of the width of the gradient background</span>
<span class="n">canvas</span><span class="p">.</span><span class="nf">Mutate</span><span class="p">(</span><span class="n">ctx</span> <span class="p">=&gt;</span> <span class="n">ctx</span><span class="p">.</span><span class="nf">DrawImage</span><span class="p">(</span><span class="n">logoImage</span><span class="p">,</span> <span class="k">new</span> <span class="nf">Point</span><span class="p">((</span><span class="n">width</span> <span class="p">/</span> <span class="m">2</span> <span class="p">/</span> <span class="m">2</span> <span class="p">-</span> <span class="n">logoWidth</span> <span class="p">/</span> <span class="m">2</span><span class="p">)</span> <span class="p">+</span> <span class="n">width</span> <span class="p">/</span> <span class="m">2</span><span class="p">,</span> <span class="m">40</span><span class="p">),</span> <span class="m">1f</span><span class="p">));</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>That’s the images taken care of. Now let’s move on to the text.</p>

<h4 id="the-text">The Text</h4>

<p>This is where we use the SixLabors Fonts library to load fonts and draw text onto the image. You work with a font via a <code class="language-plaintext highlighter-rouge">FontFamily</code>. You can load a font front from the system or from a file. Assuming the font you want to use is installed on the system, you can load it like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">fontCollection</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">FontCollection</span><span class="p">();</span>
<span class="n">fontCollection</span><span class="p">.</span><span class="nf">AddSystemFonts</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">fontFamily</span> <span class="p">=</span> <span class="n">fontCollection</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="s">"Open Sans"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now that we have the font family, we can create different <code class="language-plaintext highlighter-rouge">Font</code> instances for each text element with varying sizes and styles:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">brandFont</span> <span class="p">=</span> <span class="n">fontFamily</span><span class="p">.</span><span class="nf">CreateFont</span><span class="p">(</span><span class="m">58</span><span class="p">,</span> <span class="n">FontStyle</span><span class="p">.</span><span class="n">Bold</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">labelFont</span> <span class="p">=</span> <span class="n">fontFamily</span><span class="p">.</span><span class="nf">CreateFont</span><span class="p">(</span><span class="m">40</span><span class="p">,</span> <span class="n">FontStyle</span><span class="p">.</span><span class="n">Regular</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">nameFont</span> <span class="p">=</span> <span class="n">fontFamily</span><span class="p">.</span><span class="nf">CreateFont</span><span class="p">(</span><span class="m">48</span><span class="p">,</span> <span class="n">FontStyle</span><span class="p">.</span><span class="n">Bold</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here, we create three fonts: one for the brand text (“MoreSpeakers.com”), one for the label text (“Speaker Profile”), and one for the speaker’s name.</p>

<p>Now you can draw the text onto the canvas. For each text element, you need to specify the position where it should be drawn. I created a couple of variables to help with positioning:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="kt">float</span> <span class="n">textLeft</span> <span class="p">=</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span><span class="m">1200</span> <span class="p">/</span> <span class="m">2</span> <span class="p">+</span> <span class="m">40</span><span class="p">;</span>
<span class="kt">float</span> <span class="n">brandTop</span> <span class="p">=</span> <span class="m">200</span><span class="p">;</span>
<span class="kt">float</span> <span class="n">labelTop</span> <span class="p">=</span> <span class="n">brandTop</span> <span class="p">+</span> <span class="m">70</span><span class="p">;</span>
<span class="kt">float</span> <span class="n">nameTop</span> <span class="p">=</span> <span class="n">labelTop</span> <span class="p">+</span> <span class="m">90</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now let’s draw each text element:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="rouge-code"><pre><span class="n">canvas</span><span class="p">.</span><span class="nf">Mutate</span><span class="p">(</span><span class="n">ctx</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="c1">// MoreSpeakers</span>
    <span class="n">ctx</span><span class="p">.</span><span class="nf">DrawText</span><span class="p">(</span><span class="k">new</span> <span class="nf">RichTextOptions</span><span class="p">(</span><span class="n">brandFont</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Origin</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">PointF</span><span class="p">(</span><span class="n">textLeft</span><span class="p">,</span> <span class="n">brandTop</span><span class="p">),</span>
        <span class="n">HorizontalAlignment</span> <span class="p">=</span> <span class="n">HorizontalAlignment</span><span class="p">.</span><span class="n">Left</span>
    <span class="p">},</span> <span class="s">"MoreSpeakers.com"</span><span class="p">,</span> <span class="n">Color</span><span class="p">.</span><span class="n">White</span><span class="p">);</span>

    <span class="c1">// Speaker Profile</span>
    <span class="n">ctx</span><span class="p">.</span><span class="nf">DrawText</span><span class="p">(</span><span class="k">new</span> <span class="nf">RichTextOptions</span><span class="p">(</span><span class="n">labelFont</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Origin</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">PointF</span><span class="p">(</span><span class="n">textLeft</span><span class="p">,</span> <span class="n">labelTop</span><span class="p">),</span>
        <span class="n">HorizontalAlignment</span> <span class="p">=</span> <span class="n">HorizontalAlignment</span><span class="p">.</span><span class="n">Left</span>
    <span class="p">},</span> <span class="s">"Speaker Profile"</span><span class="p">,</span> <span class="n">Color</span><span class="p">.</span><span class="n">White</span><span class="p">);</span>

    <span class="c1">// Speaker Name (dynamic)</span>
    <span class="n">ctx</span><span class="p">.</span><span class="nf">DrawText</span><span class="p">(</span><span class="k">new</span> <span class="nf">RichTextOptions</span><span class="p">(</span><span class="n">nameFont</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">Origin</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">PointF</span><span class="p">(</span><span class="n">textLeft</span><span class="p">,</span> <span class="n">nameTop</span><span class="p">),</span>
        <span class="n">HorizontalAlignment</span> <span class="p">=</span> <span class="n">HorizontalAlignment</span><span class="p">.</span><span class="n">Left</span><span class="p">,</span>
        <span class="n">WrappingLength</span> <span class="p">=</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span> <span class="n">width</span> <span class="p">/</span> <span class="m">2</span> <span class="p">-</span> <span class="m">80</span>
    <span class="p">},</span> <span class="n">speakerName</span><span class="p">,</span> <span class="n">Color</span><span class="p">.</span><span class="n">White</span><span class="p">);</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>On the canvas, we use the <code class="language-plaintext highlighter-rouge">DrawText</code> method to draw each text element. We specify the font, position, and color for each text. The <code class="language-plaintext highlighter-rouge">RichTextOptions</code> allows us to set additional options like horizontal alignment and wrapping length. There are more options available in the <a href="https://docs.sixlabors.com/api/Fonts/SixLabors.Fonts.TextOptions.html" target="_blank">ImageSharp Fonts documentation</a>.</p>

<h4 id="the-final-step-saving-the-image">The final step: Saving the Image</h4>

<p>After drawing all the images and text onto the canvas, the final step is to save the generated Open Graph profile image to a file. You can do this using the <code class="language-plaintext highlighter-rouge">Save</code> method provided by ImageSharp. Here’s how you can save the image as a PNG file:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">canvas</span><span class="p">.</span><span class="nf">Save</span><span class="p">(</span><span class="s">"og-profile-image.png"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>There are several other image formats you can save to, such as JPEG, BMP, and GIF. You can specify the format by using the appropriate encoder when saving the image. Look at the ImageSharp Formats Extensions source <a href="https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Formats/_Generated/ImageExtensions.Save.cs" target="_blank">code</a> for more details on saving images in different formats.</p>

<h2 id="the-sample-project">The Sample Project</h2>

<p>I built a sample solution with C#10 called OpenGraphProfileImageGenerator, which is available at this <a href="https://github.com/jguadagno/OpenGraphProfileImageGenerator" target="_blank">GitHub repository</a>. The solution has 3 projects in it:</p>

<ul>
  <li><strong>OpenGraphProfileImageGenerator.Manager</strong>: Contains the core logic for generating the Open Graph profile image.</li>
  <li><strong>OpenGraphProfileImageGenerator.Manager</strong>: Contains unit tests for the Manager project.</li>
  <li><strong>OpenGraphProfileImageGenerator.ConsoleApp</strong>: This is the main project that contains the code to generate the Open Graph profile image.</li>
</ul>

<p>The console app will allow you to generate the sample image shown above.  The <code class="language-plaintext highlighter-rouge">Manager</code> project has a lot of helper methods to make it easier to generate the image.  You can clone the repository and run the console app to see how it works.</p>

<p>If you want to modify image or play around with it, all of the generation code that I walked through above is in the <code class="language-plaintext highlighter-rouge">OpenGraphImageGenerator</code> class in the <code class="language-plaintext highlighter-rouge">Manager</code> project. Look for the <code class="language-plaintext highlighter-rouge">GenerateSpeakerProfile</code> method with the following signature:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">Image</span> <span class="nf">GenerateSpeakerProfile</span><span class="p">(</span><span class="n">Image</span> <span class="n">speakerImage</span><span class="p">,</span> <span class="n">Image</span> <span class="n">logoImage</span><span class="p">,</span> <span class="kt">string</span> <span class="n">speakerName</span><span class="p">,</span>
    <span class="n">FontFamily</span> <span class="n">fontFamily</span><span class="p">,</span>
    <span class="kt">int</span> <span class="n">width</span> <span class="p">=</span> <span class="n">DefaultOpenGraphWidth</span><span class="p">,</span> <span class="kt">int</span> <span class="n">height</span> <span class="p">=</span> <span class="n">DefaultOpenGraphHeight</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="wrap-up">Wrap Up</h2>

<p>In this post, I walked you through how I used C# and the SixLabors ImageSharp library to generate dynamic Open Graph profile images for the MoreSpeakers platform. By leveraging the power of C# and ImageSharp, I was able to create visually appealing images that enhance the sharing experience on social media platforms.</p>

<h2 id="references">References</h2>

<ul>
  <li>OpenGraphProfileImageGenerator <a href="https://github.com/jguadagno/OpenGraphProfileImageGenerator" target="_blank">repository</a></li>
  <li>Open Graph protocol <a href="https://ogp.me/" target="_blank">specification</a></li>
  <li>SixLabors ImageSharp <a href="https://docs.sixlabors.com/" target="_blank">documentation</a></li>
  <li>MoreSpeakers <a href="https://morespeakers.com" target="_blank">website</a></li>
  <li>MoreSpeakers <a href="https://github.com/cwoodruff/morespeakers-com" target="_blank">GitHub repository</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET" /><category term="dotnet" /><category term="Development" /><category term="csharp" /><summary type="html"><![CDATA[Learn how to create dynamic Open Graph profile images using C# and .NET in this step-by-step guide.]]></summary></entry><entry><title type="html">My JetBrains Rider Plugins—August 2025 Edition</title><link href="https://www.josephguadagno.net/2025/08/23/my-jetbrains-rider-plugins-august-2025-edition" rel="alternate" type="text/html" title="My JetBrains Rider Plugins—August 2025 Edition" /><published>2025-08-23T17:06:00-07:00</published><updated>2025-08-23T17:06:00-07:00</updated><id>https://www.josephguadagno.net/2025/08/23/my-jetbrains-rider-plugins-august-2025-edition</id><content type="html" xml:base="https://www.josephguadagno.net/2025/08/23/my-jetbrains-rider-plugins-august-2025-edition"><![CDATA[<p>I’ve been using <a href="https://www.jetbrains.com/" target="_blank">JetBrains</a> <a href="https://www.jetbrains.com/rider/" target="_blank">Rider</a> for quite some time now.</p>

<p>I started using JetBrains Rider because I used both a Windows PC and a Mac PC and I wanted a consistent experience between the both.
JetBrains Rider provided that and now I am hooked.</p>

<p>I thought I’d take some time to blog about the plugins I use to help with my day-to-day coding (mostly in Microsoft .NET and Azure), presenting (public speaking), and blogging (using <a href="https://jekyllrb.com/" target="_blank">Jekyll</a>).</p>

<p>All of these plugins can be downloaded from either the JetBrains plugin <a href="https://plugins.jetbrains.com/" target="_blank">Marketplace</a> or directly in the IDE.</p>

<p class="notice">This is an updated post from a previous post I wrote in <a href="/2022/06/23/my-jetbrains-rider-plugins-june-2022-edition">June 2022</a> and <a href="/2024/07/12/my-jetbrains-rider-plugins-july-2024-edition" target="_blank">July 2024</a>.</p>

<p>So here is the list.</p>

<h2 id="general-development">General Development</h2>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/9525--env-files-support" target="_blank">.env files support</a></td>
      <td>As the name implies, it provides environment variable completion for <code class="language-plaintext highlighter-rouge">Dockerfile</code> and <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> files</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/7495--ignore" target="_blank">.ignore</a></td>
      <td>Allows you to manage your <code class="language-plaintext highlighter-rouge">.gitignore</code> files in a more efficient way</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/bundles/8-big-data-tools" target="_blank">Big Data Tools</a></td>
      <td>Provides tools for working with big data technologies such as Hadoop and Spark.</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/9746-ideolog" target="_blank">Ideolog</a></td>
      <td>Provides enhanced log file viewing and analysis capabilities within the IDE.</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/9792-key-promoter-x" target="_blank">Key Promoter X</a></td>
      <td>Let’s you know if there is a keystroke shortcut for any mouse based IDE commands</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/7287-karma" target="_blank">Karma</a></td>
      <td>Provides integration with the Karma test runner for JavaScript and TypeScript projects.</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/27488-opentelemetry" target="_blank">OpenTelemetry</a></td>
      <td>Provides tools for collecting, processing, and exporting telemetry data from applications. <strong><em>Note</em></strong>: This plugin is currently in a beta</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/12832-structured-logging" target="_blank">Structured Logging</a></td>
      <td>Contains some useful analyzers for structured logging. Supports Serilog, NLog, and Microsoft.Extensions.Logging</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/7808-terraform-and-hcl" target="_blank">Terraform and HCL</a></td>
      <td>Improved Terraform and OpenTofu support is tailored for developers, SREs, and DevOps teams, supporting infrastructure management with features like syntax highlighting, code completion, and real-time error checking.</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/24653-uno-platform" target="_blank">Uno Platform</a></td>
      <td>The Uno Platform plugin for JetBrains Rider enables Hot Reload support for Uno Platform apps, streamlining the development of cross-platform .NET Mobile, Web, and Desktop Apps.</td>
    </tr>
  </tbody>
</table>

<h2 id="ai-tools">AI Tools</h2>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/17718-github-copilot" target="_blank">GitHub Copilot</a></td>
      <td>GitHub Copilot uses OpenAI Codex to suggest code and entire functions in real-time right from your editor. <strong><em>Note</em></strong>: This plugin is free but requires a paid subscription service</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/22282-jetbrains-ai-assistant" target="_blank">JetBrains AI Assistant</a></td>
      <td>JetBrains AI Assistant provides AI-powered features for software development based on the JetBrains AI Service. The service transparently connects you, as an IDE user, to different large language models (LLMs). <strong><em>Note</em></strong>: This plugin is free but requires a paid subscription service</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/26104-jetbrains-junie" target="_blank">JetBrains Junie</a></td>
      <td>JetBrains Junie is your coding agent by JetBrains designed to handle tasks autonomously or in collaboration with a developer.</td>
    </tr>
  </tbody>
</table>

<h3 id="microsoft-specific-development">Microsoft Specific Development</h3>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/23289--net-aspire" target="_blank">.NET Aspire</a></td>
      <td>Provides support for .NET Aspire in Rider</td>
    </tr>
  </tbody>
</table>

<h3 id="azure-specific-development">Azure Specific Development</h3>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/13984-application-insights-debug-log-viewer" target="_blank">Application Insights Debug Log Viewer</a></td>
      <td>Provides the ability to view Azure Monitor (Application Insights) telemetry in the IDE</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/11220-azure-toolkit-for-rider" target="_blank">Azure Toolkit for Rider</a></td>
      <td>Rider plugin for integration with Azure cloud services. Allow to create, configure, and deploy .Net Core and .Net Web Apps to Azure from Rider on all supported platforms</td>
    </tr>
  </tbody>
</table>

<h2 id="presenting">Presenting</h2>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/18045-window-arranger" target="_blank">Window Arranger</a></td>
      <td>This plugin helps you easily switch between projects, arrange project windows, and resize them to various predefined dimensions. <strong><em>Note</em></strong>: This plugin used to be called <em>Window Resizer</em></td>
    </tr>
  </tbody>
</table>

<h2 id="writing">Writing</h2>

<p>I know longer have anything in this category since I use GitHub Copilot and JetBrains AI Assistant.</p>

<h2 id="no-longer-in-the-list">No longer in the list</h2>

<table>
  <tbody>
    <tr>
      <td>Plugin</td>
      <td>What it does</td>
      <td>Why did it drop</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/7345-presentation-assistant" target="_blank">Presentation Assistant</a></td>
      <td>This plugin shows name and Win/Mac shortcuts of any action you invoke</td>
      <td>Built into the IDE now</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/10249-powershell" target="_blank">PowerShell</a></td>
      <td>Provides PowerShell intellisense and script execution support for IntelliJ IDEs</td>
      <td>Turns out I don’t edit PowerShell scripts that often</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/10080-rainbow-brackets" target="_blank">Rainbow Brackets</a></td>
      <td>Provides colored brackets, parentheses, and lines in the IDE</td>
      <td>Built into the IDE now</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/2162-string-manipulation" target="_blank">String Manipulation</a></td>
      <td>Case switching, sorting, filtering, incrementing, aligning to columns, grepping, escaping, encoding… Very helpful when working with tabular data</td>
      <td>No longer a need for this plugin</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/7981-azure-devops" target="_blank">Azure DevOps</a></td>
      <td>Azure DevOps is a plugin to enable working with Git and TFVC repositories on Azure DevOps Services or Team Foundation Server (TFS) 2015+</td>
      <td>Built into the IDE now</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/12175-grazie" target="_blank">Grazie</a></td>
      <td>Intelligent spelling and grammar checks for any text you write in the IDE</td>
      <td>Between Github CoPilot and JetBrains AI Assistant, it covers what I need</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/16136-grazie-professional" target="_blank">Grazie Professional</a></td>
      <td>Enhances the base Grazie plugin with advanced writing assistance for English text in your IDE.</td>
      <td>Between Github CoPilot and JetBrains AI Assistant, it covers what I need</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/10183--net-core-user-secrets" target="_blank">.NET Core User Secrets</a></td>
      <td>Provides the ability to create and edit user secrets in .NET projects</td>
      <td>Built into the IDE now</td>
    </tr>
  </tbody>
</table>

<h2 id="wrap-up">Wrap Up</h2>

<p>You’ll also notice that a lot of the plugins I used to use are now built into the IDE.</p>

<p>Do you have a favorite not listed? Let me know.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="JetBrains" /><category term="Rider" /><category term="Plugin" /><category term="presenting" /><summary type="html"><![CDATA[I’ve been using JetBrains Rider for quite some time now.]]></summary></entry><entry><title type="html">Aspire Dashboard</title><link href="https://www.josephguadagno.net/2025/07/19/aspire-dashboard" rel="alternate" type="text/html" title="Aspire Dashboard" /><published>2025-07-19T09:35:00-07:00</published><updated>2025-07-19T09:35:00-07:00</updated><id>https://www.josephguadagno.net/2025/07/19/aspire-dashboard</id><content type="html" xml:base="https://www.josephguadagno.net/2025/07/19/aspire-dashboard"><![CDATA[<p>In a previous <a href="/2025/07/08/simplify-your-dot-net-development-with-aspire" target="_blank">post</a>, we explored how <a href="https://learn.microsoft.com/en-us/dotnet/aspire/?WT.mc_id=DT-MVP-4024623" target="_blank">Aspire</a> simplifies .NET development by managing dependencies and enhancing productivity. Today, we will delve into the Aspire Dashboard, a powerful tool that provides insights into your .NET projects.</p>

<h2 id="what-is-the-aspire-dashboard">What is the Aspire Dashboard?</h2>

<p>The Aspire Dashboard is a web-based interface that allows developers to monitor and manage their .NET projects. It provides a comprehensive overview of your projects, including their status, dependencies, and performance metrics. The dashboard is designed to help you quickly identify issues and optimize your development workflow.</p>

<p>The dashboard is available once you start the AppHost project in your solution. For this post, we will use the sample project from the <a href="/2025/07/08/simplify-your-dot-net-development-with-aspire" target="_blank">Simplify Your .NET Development with Aspire</a> post which is available on <a href="https://github.com/jguadagno/contacts-noauth" target="_blank">GitHub</a></p>

<p>Once we start the AppHost project, Aspire will launch the Aspire dashboard in the browser as well as other projects that are part of the solution. In this case, the <em>Contacts.Api</em> and <em>Contacts.Web</em> projects will be launched.  The Aspire dashboard should look similar to the following image:</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/initial-aspire-dashboard-section-names.png" alt="Aspire Dashboard - Overview" class="align-center" /></p>

<h2 id="exploring-the-dashboard">Exploring the Dashboard</h2>

<p>The Aspire Dashboard is divided into several sections, each providing different pieces of information about your projects:</p>

<ul>
  <li><strong>1 - Title Bar</strong>: Displays the name of the project.  This currently is the name of the <em>AppHost</em> project, less the <code class="language-plaintext highlighter-rouge">.AppHost</code> suffix.  In this case, it is <em>Contacts</em>.</li>
  <li><strong>2 - Settings &amp; More</strong>: This section provides access to dashboard settings and more options.</li>
  <li><strong>3 - Menu</strong>: Contains links to various sections of the dashboard, such as resources, dependencies, and performance metrics.</li>
</ul>

<p>Let’s explore each section in more detail.</p>

<h3 id="1-title-bar">1. Title Bar</h3>

<p>The title bar displays the name of the project. This is useful for quickly identifying which project you are currently viewing in the dashboard. Not much more to say about this section.</p>

<h3 id="2-settings--more">2. Settings &amp; More</h3>

<p>This section provides access to various settings and options for the dashboard.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/settings-and-more.png" alt="Aspire Dashboard - Settings &amp; More" class="align-center" /></p>

<p>The GitHub icon (#1) allows you to view the source code for Aspire on <a href="https://aka.ms/dotnet/aspire/repo" target="_blank">GitHub</a>.</p>

<p>The Help icon (#2) provides a list of the available keyboard shortcuts for navigating the dashboard. This is useful for power users who prefer to use keyboard shortcuts instead of the mouse. It also provides a link to the <a href="https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/dashboard/overview?WT.mc_idDT-MVP-4024623" target="_blank">Aspire documentation</a>. Not that you will need it after this post. :smile:</p>

<p>The GitHub Copilot icon (#3) starts up a GitHub Copilot chat session that lets you diagnose issues with your application. Summarize the application and it’s dependencies, and ask questions about the application. This is a great way to get help with your project without leaving the dashboard.  Unfortunately, this feature requires that you have a GitHub Copilot subscription, as well as start the application from Visual Studio 14.14 or later or VS Code with the C# Dev Kit 1.19.63 or later.</p>

<p>The Settings icon (#4) allows you to configure various settings for the dashboard, such as theme and language. You can also delete all of the local telemetry that has been gathered through Aspire.</p>

<h3 id="3-menu">3. Menu</h3>

<p>The menu section contains links to various sections of the dashboard. This is probably the most important section of the dashboard, as it provides access to all of the features and functionality of Aspire. You’ll spend a lot of time clicking around in this section or using the keyboard shortcuts to navigate.</p>

<p>The “menu” section contains links to the following sections:</p>

<ul>
  <li><strong>Resources</strong>: This section provides an overview of the resources used by your project, such as databases, APIs, and other external dependencies.</li>
  <li><strong>Console</strong>: This section allows you to view the console logs for different resources in your project.</li>
  <li><strong>Structured</strong>: This section allows you to view structured logs for your project, if you have a structured logging provider configured in your project, such as Serilog.</li>
  <li><strong>Traces</strong>: This section allows you to view traces for your project, which correlate log events that are part of the same logical activity, even if they’re spread across multiple machines or processes.</li>
  <li><strong>Metrics</strong>: This section allows you to view performance and health metrics for your project, such as response times, error rates, and other key performance indicators.</li>
</ul>

<p>Let’s dig more into this menu.</p>

<h2 id="resources">Resources</h2>

<p>The <em>Resources</em> section provides an overview of the resources/dependencies used by your project. You can think of this as a list of all of the “things” that make up your project, such as databases, APIs, web services, and other external dependencies.</p>

<p>This section is broken down into two parts, or views: the <em>Table</em> view and the <em>Graph</em> view.</p>

<h3 id="table-view">Table View</h3>

<p>The <em>Table</em> view displays a list of all the resources used by your project in a tabular format. This view is useful for quickly identifying the resources and their status. You can also filter the resources by type, status, and other criteria, start/stop the resource, and view the details of each resource.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/resource-view-table.png" alt="Aspire Dashboard - Table View" class="align-center" /></p>

<h4 id="filtering-resources">Filtering Resources</h4>

<p>In the filter text box, upper right hand corner, you can filter the resources by name. For example, if you type “sql” in the filter text box, the table will only show resources that contain “sql” in their name. The button to the right of the filter allows you to filter by resource type, such as SQL Server, Redis, or RabbitMQ. This is useful for quickly finding the resources you are interested in. The second button to the right of the filter allows for to customize what is visible to the current view, like the resource type or child dependencies.</p>

<h4 id="resource-details">Resource Details</h4>

<p>The resource table displays the following information for each resource:</p>

<ul>
  <li><strong>Name</strong>: The name of the resource. This is the name of the resource as you defined it in your project. The name column can also contain child resources. In this example, the <em>AzureStorage</em> resource has a child resource <code class="language-plaintext highlighter-rouge">cwjgcontacts</code> which is a storage name in the Azure Storage account, which has two child resources, <code class="language-plaintext highlighter-rouge">contact-images</code> and <code class="language-plaintext highlighter-rouge">contact-images-thumbnails</code>, which are the containers in the Azure Storage account.</li>
  <li><strong>State</strong>: The current state of the resource.
    <ul>
      <li>This can be <em>Running</em>, <em>Unhealthy</em>, <em>Error</em>, or <em>Stopped</em>.
        <ul>
          <li><em>Running</em> means the resource is up and running.</li>
          <li><em>Unhealthy</em> generally means the resource is starting up, but not yet ready to accept requests.</li>
          <li><em>Error</em> means there was an issue starting the resource.</li>
          <li><em>Stopped</em> means the resource is not running.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>Start Time</strong>: The time the resource was started.</li>
  <li><strong>Source</strong>: The source of the resource, such as a Docker image, a local project file, or something else.</li>
  <li><strong>URLs</strong>: The URL(s) associated with the resource. You can click on the URL to open up the resource, like a web service or API, in a new tab in your browser.</li>
  <li><strong>Actions</strong>: The actions you can take on the resource, such as starting or stopping the resource. You can also view the details of the resource by clicking on the “View Details” link.</li>
</ul>

<h5 id="actions">Actions</h5>

<p>The actions column provides quick access to common actions that you can take on the resource. You can start or stop the resource, view the details of the resource, or view the logs for the resource.</p>

<p>The stop icon <img src="/assets/images/posts/2025/aspire-dashboard/stop.png" alt="Resource Stop Button" /> will stop the resource.
The console logs icon <img src="/assets/images/posts/2025/aspire-dashboard/logs.png" alt="Resource Console Log Button" /> will open the console logs for the resource in a new tab in your browser.
The ellipsis icon <img src="/assets/images/posts/2025/aspire-dashboard/more-options.png" alt="Resource More Options Button" /> will open the details for the resource in a new tab in your browser. Clicking on the ellipsis icon will bring up the actions menu. The actions menu will vary depending on the type of resource, but generally includes options to view the details of the resource, view the console logs, Ask GitHub Copilot, start, and stop the resource.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/actions.png" alt="Aspire Dashboard - Actions Menu" class="align-center" /></p>

<h6 id="view-details">View Details</h6>

<p>The <em>View Details</em> option will give you more information about the resource, such as the configuration, environment variables, and other details. In the screen shot below, you can see some of the details for the <em>Contacts.Web</em> resource.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/contacts-web-view-details.png" alt="Aspire Dashboard - View Details" class="align-center" /></p>

<p>Here you will find the following information:</p>

<ul>
  <li><strong>Resource Information</strong>: The name, type, state of the resource, and more.</li>
  <li><strong>URLs</strong>: The URLs associated with the resource. You can click on the URL to open up the resource in a new tab in your browser.</li>
  <li><strong>References</strong>: The references to other resources that this resource depends on. This is useful for understanding the relationships between resources in your project.</li>
</ul>

<p>Not shown in the screen shot above, but you can also view the back reference, health checks, and environment variables for the resource. The back reference shows you which resources depend on this resource, while the health checks show you the health of the resource. The environment variables show you the environment variables that are set for the resource.</p>

<p>Again, the information displayed will vary depending on the type of resource. For example, a SQL Server resource will have different information than a Redis resource.</p>

<h3 id="graph-view">Graph View</h3>

<p>The <em>Graph</em> view displays the resources in a graphical format, showing the relationships between the resources. This view is useful for understanding the dependencies between resources and how they interact with each other.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/resource-graph-view.png" alt="Aspire Dashboard - Graph View" class="align-center" /></p>

<p>The status of the resource can be identified by the icon in the upper right hand corner of the resource node. Hovering over the resource will show you which way the dependency flows, such as <em>Inbound</em> or <em>Outbound</em>. Clicking on the resource will show you the details of the resource in a side panel, similar to the clicking the ellipsis and selecting <em>View Details</em> in the <em>Table</em> view.</p>

<h2 id="console">Console</h2>

<p>In the <em>Console</em> section, you can view the console logs for different resources in your project.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/console-sql-server.png" alt="Aspire Dashboard - Console" class="align-center" /></p>

<p>In the screen shot above, you can see the console logs for the <em>SQL Server</em> resource. In the top part of this section, you can select the resource you want to view the logs for. You can also pause the logs, or delete the logs for this resource or any of the resources in you project. You also can perform different actions on the resource, such as starting or stopping the resource, or viewing the details of the resource.</p>

<p>In the upper right hand corner, to the right of the <em>Watching logs…</em> text, you can click on the “Console Log Settings” icon to either download the logs or show the timestamps for each log entry.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/console-log-settings.png" alt="Aspire Dashboard - Console Log Settings" class="align-center" /></p>

<h2 id="structured">Structured</h2>

<p>In the <em>Structured</em> section, you can view structured logs for your project. This only works if you have a structured logging provider configured in your project, such as Serilog. This section provides a way to view the structured logs in a more readable format.</p>

<p>This project does not have a structured logging provider. The screen shot below is from a different project that has Serilog configured.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/structured-log-view.png" alt="Aspire Dashboard - Structured Logs" class="align-center" /></p>

<p>Similar to the <em>Console</em> section, you can select the resource you want to view the logs for, pause the logs, or delete the logs for this resource or any of the resources in your project. You can also perform different actions on the resource, such as starting or stopping the resource, or viewing the details of the resource. However, since this is structured logging, you can also filter the logs by level, such as <em>Information</em>, <em>Warning</em>, <em>Error</em>, or <em>Critical</em>. You can also search for specific text in the logs.</p>

<p>You will see that there are several columns in the structured log view:</p>

<ul>
  <li><strong>Resource</strong>: The name of the resource that generated the log entry.</li>
  <li><strong>Level</strong>: The level of the log entry, such as <em>Information</em>, <em>Warning</em>, <em>Error</em>, or <em>Critical</em>.</li>
  <li><strong>Message</strong>: The message of the log entry.</li>
  <li><strong>Timestamp</strong>: The timestamp of the log entry.</li>
  <li><strong>Trace</strong>: The trace ID associated with the log entry. This is useful for correlating logs across different resources in your project.</li>
  <li><strong>Actions</strong>: The actions you can take on the log entry, such as viewing the details of the resource or copying the log entry to the clipboard.</li>
</ul>

<p>The actions ellipsis icon <img src="/assets/images/posts/2025/aspire-dashboard/more-options.png" alt="Structured Log Actions Button" /> will open the actions menu, you can:</p>

<ul>
  <li><strong>View Details</strong>: which allows you to view the details of the message, it provides more details about the log entry, very similar to the Application Insights interface on the Azure portal.</li>
  <li><strong>Log Message</strong>: which allows you to copy the log message to the clipboard or view the log message in different formats, such as JSON or XML.</li>
  <li><strong>Ask GitHub Copilot</strong>: which allows you to ask GitHub Copilot about the log entry.</li>
</ul>

<h2 id="traces">Traces</h2>

<p>Traces correlate log events that are part of the same logical activity (e.g. the handling of a single request), even if they’re spread across multiple machines or processes.</p>

<p>In this section, you can view the traces for your project. This is useful for understanding the flow of requests through your application and how they interact with different resources. You can filter the traces by resource, status, and other criteria.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/traces.png" alt="Aspire Dashboard - Traces" class="align-center" /></p>

<p>Here you will see that Aspire captured six traces for the <em>Contacts.Web</em> resource and are grouped by a common trace ID. The trace ID is a unique identifier for the trace, and it is not shown in the screen shot above.  In this view, you can see the following information:</p>

<ul>
  <li><strong>Timestamp</strong>: When the first log entry was captured for the trace.</li>
  <li><strong>Name</strong>: The name of the trace, which is the name of the resource and an action that generated the trace.</li>
  <li><strong>Spans</strong>: All of the the different resources, and their count of log entries, that were part of the trace.</li>
  <li><strong>Duration</strong>: The total duration of the trace, which is the time between the first and last log entry in the trace.</li>
  <li><strong>Actions</strong>: The actions you can take on the trace, such as viewing the details of the trace or copying</li>
</ul>

<p>For the actions, you can click on the ellipsis icon <img src="/assets/images/posts/2025/aspire-dashboard/more-options.png" alt="Trace Actions Button" /> to view the actions menu. The actions menu will allow you to:</p>

<ul>
  <li><strong>View Details</strong>: which allows you to view the details of the trace, including the log entries that are part of the trace.</li>
  <li><strong>Structured Log</strong>: which allows you to view the structured logs for the trace.</li>
  <li><strong>Ask GitHub Copilot</strong>: which allows you to ask GitHub Copilot about the trace.</li>
</ul>

<p>If you click on one of the traces, you will see the details of the trace.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/traces-details.png" alt="Aspire Dashboard - Trace Details" class="align-center" /></p>

<p>In this view, you will see details about the trace, such as the trace ID, the timestamp, the duration, and the spans that are part of the trace. You can also see the log entries that are part of the trace, which are ordered in the order they happened.</p>

<p>In the screen shot above, you can see that the trace is made up of three spans, which are the <em>Contacts.Web</em>, the initiator of the trace, a <em>PUT</em> call to Azure Storage, and then a <em>GET</em> call to the <em>Contacts.Api</em> resources. This is useful for understanding the flow of requests through your application and how they interact with different resources as well as the time it took to complete each portion of the flow.</p>

<h2 id="metrics">Metrics</h2>

<p>Metrics expose the performance and health characteristics of an app as simple numerical values. As a result, they have low performance overhead and many services configure them as always-on telemetry. This also makes them suitable for triggering alerts when potential problems are detected.</p>

<p>In this section, you can view the metrics for your project. You can filter the metrics by resource and the time duration. Example: the last minute, last five minutes, last hour, or last 12 hours. You can also view the metrics in a graphical format, which is useful for understanding the performance and health of your application.</p>

<p><img src="/assets/images/posts/2025/aspire-dashboard/metrics.png" alt="Aspire Dashboard - Metrics" class="align-center" /></p>

<p>On the left side of the screen, you can see the list of metrics that are available for your project. You can select the metric you want to view, grouped by the metric collector, such as <em>Microsoft.AspNetCore.Hosting</em>, <em>Microsoft.AspNetCore.Routing</em>, and others. The right side of the screen shows the graph or table for the selected metric.  Depending on the metric chosen, you can several data points, such as the number of requests, the response time, and the number of errors. There are also several filters available to help you narrow down the metrics you want to view.</p>

<p>In the screen shot above we are looking at the <em>Microsoft.AspNetCore.Hosting</em> - <em>http.server.active_requests</em> metric, which shows the number of active requests being processed by the <em>Contacts.Web</em> resource. When the screen shot was taken, there were no active requests being processed, so the graph is empty. If there were active requests, you would see a line graph showing the number of active requests over time.  The screen shot also lists different filters that are available for this metrics, such as <em>http.request.method</em> and <em>url.scheme</em>. These filters are going to vary based on the metric you are viewing. You can use these filters to narrow down the metrics you want to view.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>The Aspire Dashboard is a powerful tool that provides insights into your .NET projects. It allows you to monitor and manage your resources, dependencies, and performance metrics in a single interface. The dashboard is designed to help you quickly identify issues and optimize your development workflow.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/en-us/dotnet/aspire/?WT.mc_id=DT-MVP-4024623" target="_blank">.NET Aspire Documentation</a></li>
  <li><a href="https://www.youtube.com/watch?v=ZKgUwDWCtic" target="_blank">YouTube - AspiriFridays (Joe Guadagno’s Broadcast site)</a></li>
  <li><a href="/2025/07/08/simplify-your-dot-net-development-with-aspire" target="_blank">Simplify Your .NET Development with Aspire</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Aspire" /><category term=".NET" /><category term="dotnet" /><category term="Development" /><category term="ASP.NET" /><category term="Containers" /><summary type="html"><![CDATA[Let's explore the Aspire Dashboard for insights into your .NET projects.]]></summary></entry><entry><title type="html">Simplify Your .NET Development with Aspire</title><link href="https://www.josephguadagno.net/2025/07/08/simplify-your-dot-net-development-with-aspire" rel="alternate" type="text/html" title="Simplify Your .NET Development with Aspire" /><published>2025-07-08T19:59:00-07:00</published><updated>2025-08-02T14:16:00-07:00</updated><id>https://www.josephguadagno.net/2025/07/08/simplify-your-dot-net-development-with-aspire</id><content type="html" xml:base="https://www.josephguadagno.net/2025/07/08/simplify-your-dot-net-development-with-aspire"><![CDATA[<p>Aspire is a new tool that simplifies the development of .NET applications by managing dependencies and configurations, allowing developers to focus on building features rather than wrestling with setup and environment issues.</p>

<p>According to the <a href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview?WT.mc_id=DT-MVP-4024623" target="_blank">official documentation</a>, .NET Aspire provides tools, templates, and packages to help you build observable, production-ready apps. Delivered through NuGet packages, .NET Aspire simplifies common challenges in modern app development.</p>

<p>A few weeks ago, I was on <a href="https://www.youtube.com/watch?v=ZKgUwDWCtic" target="_blank">AspiriFridays</a> where <a href="https://bsky.app/profile/damianedwards.com" target="_blank">Damian Edward</a>, <a href="https://bsky.app/profile/davidfowl.com" target="_blank">David Fowler</a>, <a href="https://bsky.app/profile/maddymontaquila.net" target="_blank">Maddy Montaquila</a> and I were adding .NET <a href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview?WT.mc_id=DT-MVP-4024623" target="_blank">Aspire</a> to the <a href="https://github.com/jguadagno/jjgnet-broadcast/" target="_blank">JosephGuadagno.NET Broadcasting</a> application. Let’s take a look at how Aspire can help you streamline your .NET development process.</p>

<p class="notice--info"><strong><em>Note</em></strong>: This blog post was updated on August 2nd to reflect Aspire version 9.4.0</p>

<h2 id="getting-started-with-aspire">Getting Started with Aspire</h2>

<p>To get started with Aspire, you need a few prerequisites:</p>

<ul>
  <li><a href="https://dotnet.microsoft.com/download?WT.mc_id=DT-MVP-4024623" target="_blank">.NET SDK</a> (version 8.0 or later)</li>
  <li>An OCI compliant container runtime, such as:
    <ul>
      <li><a href="https://www.docker.com/products/docker-desktop" target="_blank">Docker</a> (for containerization) or</li>
      <li><a href="https://podman.io/" target="_blank">Podman</a> (if you prefer)</li>
    </ul>
  </li>
  <li>An IDE or code editor of your choice, such as:
    <ul>
      <li><a href="https://visualstudio.microsoft.com/vs/?WT.mc_id=DT-MVP-4024623" target="_blank">Visual Studio</a> version 17.9 or later</li>
      <li><a href="https://code.visualstudio.com/?WT.mc_id=DT-MVP-4024623" target="_blank">Visual Studio Code</a></li>
      <li><a href="https://www.jetbrains.com/rider/" target="_blank">JetBrains Rider</a> with the <a href="https://plugins.jetbrains.com/plugin/23289--net-aspire" target="_blank">.NET Aspire plugin</a></li>
    </ul>
  </li>
</ul>

<p class="notice--info"><strong><em>Note</em></strong>: .NET Aspire also works with <a href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/github-codespaces?WT.mc_id=DT-MVP-4024623" target="_blank">GitHub Codespaces</a> or <a href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/dev-containers?WT.mc_id=DT-MVP-4024623" target="_blank">Dev Containers</a>.</p>

<p>If you want to use the Aspire project templates, you can install them with the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet new <span class="nb">install </span>Aspire.ProjectTemplates <span class="nt">--force</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>Note</em></strong>: These get updated frequently, so you may want to run this command periodically to ensure you have the latest templates.</p>

<p>I recommend installing the Aspire CLI tool, which provides a set of commands to help you manage your Aspire projects. You can install the CLI tool by running the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet tool <span class="nb">install</span> <span class="nt">-g</span> Aspire.Cli <span class="nt">--prerelease</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now we are ready to Aspire-ify our project!</p>

<h2 id="the-project">The Project</h2>

<p>I have a sample project that I use for presentations and demos. It’s a simple ASP.NET Core web application that allows users to add, edit, delete, and list contacts.  The project was designed to be a straightforward example of how to build a web application using .NET technologies, and it serves as a great starting point for learning .NET.</p>

<p>The project is built using .NET 9 and leverages various technologies such as:</p>

<ul>
  <li><a href="https://dotnet.microsoft.com/apps/aspnet?WT.mc_id=DT-MVP-402462" target="_blank">ASP.NET Core</a> for the web application</li>
  <li><a href="https://learn.microsoft.com/en-us/ef/core/?WT.mc_id=DT-MVP-4024623" target="_blank">Entity Framework Core</a> for data access</li>
  <li>An assortment of <a href="https://azure.microsoft.com/?WT.mc_id=DT-MVP-4024623" target="_blank">Azure</a> cloud services</li>
</ul>

<p>You can find the project <a href="https://github.com/jguadagno/contacts-noauth" target="_blank">contacts-noauth</a> on GitHub. If you want to follow along, you can clone the repository and open it in your preferred IDE or code editor.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>git clone https://github.com/jguadagno/contacts-noauth.git
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="adding-aspire-to-the-project">Adding Aspire to the Project</h2>

<p>Aspire project templates are designed to help you quickly set up a new project with the necessary dependencies and configurations for building .NET applications. They provide a starting point for your application, allowing you to focus on building features rather than wrestling with setup and environment issues.  There are two projects that are created when you add Aspire to your solution:</p>

<ul>
  <li><strong>App Host</strong>: This project serves as the entry point for your Aspire application. It contains the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file, which is where you configure your application and its services.</li>
  <li><strong>Service Defaults</strong>: This project sets up the default services for your Aspire application, such as telemetry, logging, health checks, and more. It contains the <code class="language-plaintext highlighter-rouge">Extensions.cs</code> file that sets up these services.</li>
</ul>

<p>Depending on the IDE you are using, you can add Aspire to your project in different ways. Here are the steps for Visual Studio, Visual Studio Code, and JetBrains Rider.</p>

<h3 id="visual-studio">Visual Studio</h3>

<p>Visual Studio provides a straightforward way to add Aspire to your project using the built-in project templates and in my opinion, is the easiest way to get started with Aspire.</p>

<p>In Visual Studio, in the Solution Explorer, right-click on the <em>Contacts.Api</em> project and select “Add” &gt; “.NET Aspire Orchestration Support”. This will open the “Add .NET Aspire Orchestration Support” dialog.</p>

<p><img src="/assets/images/posts/2025/simplify-your-dot-net-development-with-aspire/aspire-create-project-visual-studio.png" alt="Visual Studio - Add .NET Aspire Orchestration Support" class="align-center" /></p>

<p>The defaults are fine, so you can click “Add” to create the Aspire project. This will create two new projects in your solution called <em>Contacts.AppHost</em> and <em>Contacts.ServiceDefaults</em> then add the necessary references to the <em>Contacts.Api</em> project.</p>

<p>Once the projects are created, you will see them in the Solution Explorer. The <em>Contacts.AppHost</em> project contains the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file, which is where you configure your application and its services. The <em>Contacts.ServiceDefaults</em> project contains the <code class="language-plaintext highlighter-rouge">Extensions.cs</code> file, which sets up the default services for your Aspire application.  Visual Studio also adds two new run configurations to the solution, one for HTTP and one for HTTPS. You can use these configurations to run your Aspire application. In addition, Visual Studio will display the Aspire welcome page which provides an overview of the Aspire projects and how to use them.</p>

<h3 id="visual-studio-code">Visual Studio Code</h3>

<p>With the solution open in Visual Studio Code, you can add Aspire to the project by opening the terminal and running the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet new aspire-host <span class="nt">-o</span> Contacts.AppHost
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then add the app host project to the solution:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet sln add .<span class="se">\C</span>ontacts.sln add .<span class="se">\C</span>ontacts.AppHost<span class="se">\C</span>ontacts.AppHost.csproj
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now, let’s add the Service Defaults project to the solution. In the terminal, run the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet new aspire-servicedefaults <span class="nt">-o</span> Contacts.ServiceDefaults
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then add the service defaults project to the solution:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet sln add .<span class="se">\C</span>ontacts.sln add .<span class="se">\C</span>ontacts.ServiceDefaults<span class="se">\C</span>ontacts.ServiceDefaults.csproj
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You’ll need to add the <em>Contacts.ServiceDefaults</em> project reference to both the <em>Contacts.Api</em> and the <em>Contacts.WebUi</em> projects. You can do this by running the following commands in the terminal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>dotnet add .<span class="se">\C</span>ontacts.Api<span class="se">\C</span>ontacts.Api.csproj reference .<span class="se">\C</span>ontacts.ServiceDefaults<span class="se">\C</span>ontacts.ServiceDefaults.csproj
dotnet add .<span class="se">\C</span>ontacts.WebUi<span class="se">\C</span>ontacts.WebUi.csproj reference .<span class="se">\C</span>ontacts.ServiceDefaults<span class="se">\C</span>ontacts.ServiceDefaults.csproj
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now we should have the Aspire projects added to the solution. You can verify this by opening the Solution Explorer in Visual Studio Code and checking that the <em>Contacts.AppHost</em> and <em>Contacts.ServiceDefaults</em> projects are listed. If so, we are ready to run the application.</p>

<h3 id="jetbrains-rider">JetBrains Rider</h3>

<p>With the solution open in JetBrains Rider, you can add Aspire to the project by right-clicking on the solution in the Solution Explorer and selecting “Add” &gt; “New Project…”. This will open the “New Project” dialog.</p>

<ul>
  <li>Select <em>Aspire</em> from the project templates on the left side.</li>
  <li>Enter a name for the project, such as <code class="language-plaintext highlighter-rouge">Contacts.AppHost</code>.</li>
  <li>Select the type of <code class="language-plaintext highlighter-rouge">App Host</code>.</li>
  <li>Click “Create” to create the project.</li>
</ul>

<p><img src="/assets/images/posts/2025/simplify-your-dot-net-development-with-aspire/aspire-create-project-rider.png" alt="JetBrains Rider - Create Project - Aspire" class="align-center" /></p>

<p>This will create the first part of what “Aspirify” your project. You will see a new project in the Solution Explorer with the name you specified.</p>

<p>Now, we need to add the Service Default project to the solution.</p>

<p>Right-click on the solution in the Solution Explorer and select “Add” &gt; “New Project…”. This will open the “New Project” dialog.</p>

<ul>
  <li>Select <em>Aspire</em> from the project templates on the left side.</li>
  <li>Enter a name for the project, such as <code class="language-plaintext highlighter-rouge">Contacts.ServiceDefaults</code>.</li>
  <li>Select the type of <code class="language-plaintext highlighter-rouge">Service Defaults</code>.</li>
  <li>Click “Create” to create the project.</li>
</ul>

<p>Now you will see a new project in the Solution Explorer with the name you specified. In that project, you will find a <code class="language-plaintext highlighter-rouge">Extensions.cs</code> file, which is where Aspire configures some of the default services.</p>

<p>You’ll also notice, that the Aspire templates added two new configuration files to the solution:</p>

<ul>
  <li>Contacts.AppHost: http</li>
  <li>Contacts.AppHost: https</li>
</ul>

<p><img src="/assets/images/posts/2025/simplify-your-dot-net-development-with-aspire/aspire-rider-run-configurations.png" alt="JetBrains Rider - Run Configurations - Aspire" class="align-center" /></p>

<p>You’ll use one of these configurations to run your Aspire application. The <code class="language-plaintext highlighter-rouge">http</code> configuration is for running the application without HTTPS, while the <code class="language-plaintext highlighter-rouge">https</code> configuration is for running the application with HTTPS.</p>

<h2 id="adding-resources-projects">Adding Resources (Projects)</h2>

<p>Now that we have the Aspire projects added to the solution, we can start adding the resources of our application. In this case, we will add support for loading the web application, API, and database components.</p>

<p class="notice--info"><strong><em>Note</em></strong>: The rest of this post will walk you through adding the components to the project. For this, it doesn’t matter which IDE you are using, as the steps are the same across Visual Studio, Visual Studio Code, and JetBrains Rider.</p>

<p>All of this is done by adding the components to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file in the <em>Contacts.AppHost</em> project. You can think of this a sort of like a <code class="language-plaintext highlighter-rouge">Startup.cs</code> or <code class="language-plaintext highlighter-rouge">Program.cs</code> file in a traditional ASP.NET Core application. This is where compose the pieces of your application together and establish any dependencies between them.</p>

<p>Let’s get started by opening the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file in the <em>Contacts.AppHost</em> project.</p>

<h3 id="adding-the-web-api-project">Adding the Web Api Project</h3>

<p>Depending on which IDE you are using, you will see a slightly different version of the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file.  If you are using Visual Studio, and you added the Aspire projects using the “Add .NET Aspire Orchestration Support” option, you will see a basic <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file that looks like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">DistributedApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_Api</span><span class="p">&gt;(</span><span class="s">"contacts-api"</span><span class="p">);</span>

<span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">().</span><span class="nf">Run</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>If you are using Visual Studio Code or JetBrains Rider, you will see a more generic <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file that looks like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">DistributedApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">().</span><span class="nf">Run</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This is the only difference between the two versions of the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file. The Visual Studio version has a line that adds the <em>Contacts.Api</em> project and reference to the application, while the Visual Studio Code and JetBrains Rider versions do not.</p>

<p>Let’s explain what happened here. The <code class="language-plaintext highlighter-rouge">DistributedApplication.CreateBuilder(args)</code>, line 1, method creates a new <code class="language-plaintext highlighter-rouge">DistributedApplicationBuilder</code> instance, which is used to configure the application. The <code class="language-plaintext highlighter-rouge">AddProject&lt;Projects.Contacts_Api&gt;("contacts-api")</code> method, line 3, adds the <em>Contacts.Api</em> project to the application, with the name of <code class="language-plaintext highlighter-rouge">contacts-api</code>, allowing it to be run as part of the Aspire application. Then the <code class="language-plaintext highlighter-rouge">builder.Build().Run()</code> method, line 5, builds the application and runs it.</p>

<p>If you are using Visual Studio Code or JetBrains Rider, you will need to add the <em>Contacts.Api</em> project as a reference to the <em>Contacts.Api</em> project, then, add the <em>project</em> the Aspire app. You can do this by adding the following line to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_Api</span><span class="p">&gt;(</span><span class="s">"contacts-api"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>Note</em></strong>: The name field, in this case, <code class="language-plaintext highlighter-rouge">contacts-api</code>, is used to identify the project in the Aspire application. You can use any name you like, but it is recommended to use a name that is descriptive and unique within your application.</p>

<p>At this point, you should have the <em>Contacts.Api</em> project added to the Aspirified application. You can verify this by running the application using the <em>http</em> or <em>https</em> run configuration in Visual Studio or <em>Contacts.AppHost: http(s)</em> in Rider.</p>

<p><strong><em>Note</em></strong>: If you are running this sample project with aspire version <code class="language-plaintext highlighter-rouge">9.3.1-preview.1.25305.6</code>, there is a bug that prevents the application from running.  You can fix this by moving <code class="language-plaintext highlighter-rouge">builder.AddServiceDefaults();</code> to just after the <code class="language-plaintext highlighter-rouge">var builder = WebApplication.CreateBuilder(args);</code> in the <em>Contacts.Api</em> <code class="language-plaintext highlighter-rouge">Program.cs</code> file, line 20.  Then delete the <code class="language-plaintext highlighter-rouge">builder.MapDefaultEndpoints();</code> in the <code class="language-plaintext highlighter-rouge">Program.cs</code> file, and add the following line just after the <code class="language-plaintext highlighter-rouge">var app = builder.Build();</code>, around line 28.</p>

<p>The <code class="language-plaintext highlighter-rouge">try</code> portion of the <code class="language-plaintext highlighter-rouge">Program.cs</code> file should look like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="rouge-code"><pre><span class="k">try</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
    <span class="n">builder</span><span class="p">.</span><span class="nf">AddServiceDefaults</span><span class="p">();</span>

    <span class="n">builder</span><span class="p">.</span><span class="n">Logging</span><span class="p">.</span><span class="nf">ClearProviders</span><span class="p">();</span>
    <span class="n">builder</span><span class="p">.</span><span class="n">Host</span><span class="p">.</span><span class="nf">UseNLog</span><span class="p">();</span>
    
    <span class="nf">ConfigureServices</span><span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">);</span>

    <span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>

    <span class="n">app</span><span class="p">.</span><span class="nf">MapDefaultEndpoints</span><span class="p">();</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">builder</span><span class="p">.</span><span class="n">Environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
    <span class="p">{</span>
        <span class="n">app</span><span class="p">.</span><span class="nf">UseDeveloperExceptionPage</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="nf">ConfigureMiddleware</span><span class="p">(</span><span class="n">app</span><span class="p">);</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">Run</span><span class="p">();</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>As of the time of this post, it is an <a href="https://github.com/dotnet/aspire/issues/10255" target="_blank">issue</a> that I submitted to the Aspire team.</p>

<p>You should be able to run the application now, and the Aspire dashboard should be accessible at a URL like <code class="language-plaintext highlighter-rouge">https://localhost:17004</code> or <code class="language-plaintext highlighter-rouge">http://localhost:15122</code>.</p>

<p class="notice--info"><strong>Note</strong>: The port numbers may vary depending on your configuration, so check the run configuration in Visual Studio or Rider to see the exact URL.</p>

<p>Upon a successful run, you should see the Aspire dashboard, which provides an overview of the application and its components. The dashboard will show the <code class="language-plaintext highlighter-rouge">contacts-api</code> project as a running service.</p>

<p><img src="/assets/images/posts/2025/simplify-your-dot-net-development-with-aspire/aspire-dashboard.png" alt="Aspire Dashboard" class="align-center" /></p>

<h4 id="dashboard-overview">Dashboard Overview</h4>

<p>The Aspire dashboard provides a comprehensive overview of your application, including:</p>

<ol>
  <li>List of running services</li>
  <li>Console output from the application</li>
  <li>Structured logging information</li>
  <li>Tracing information</li>
  <li>Metrics and health checks</li>
</ol>

<p>We’ll explore the dashboard in more detail in a future post, but for now, you can see that the <code class="language-plaintext highlighter-rouge">contacts-api</code> project is running and accessible.  If you click on the <em>URLs</em> link in the dashboard, the Api will open in a new tab, and you can see the Swagger UI for the API.</p>

<h3 id="adding-the-sql-server-project">Adding the Sql Server Project</h3>

<p>Now that we have the <em>Contacts.Api</em> project added to the Aspire application, let’s “tell” Aspire about the SQL Server database that the API will use.</p>

<p>Back to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file in the <em>Contacts.AppHost</em> project. We will add the following line to the builder after the <code class="language-plaintext highlighter-rouge">AddProject</code> line for the <em>Contacts.Api</em> project:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">sql</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">AddSqlServer</span><span class="p">(</span><span class="s">"SqlServer"</span><span class="p">)</span>   
    <span class="p">.</span><span class="nf">WithLifetime</span><span class="p">(</span><span class="n">ContainerLifetime</span><span class="p">.</span><span class="n">Persistent</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This line adds the SQL Server database to the Aspire application, with the name <code class="language-plaintext highlighter-rouge">SqlServer</code>. The <code class="language-plaintext highlighter-rouge">WithLifetime(ContainerLifetime.Persistent)</code> method specifies that the database should be persistent, meaning it will not recreate the database every time the application is run. This is useful for development and testing purposes, as it allows you to keep the data in the database between runs.</p>

<p>You may see the red squiggly line under the <code class="language-plaintext highlighter-rouge">AddSqlServer</code> method, indicating that the method is not recognized. This is because you need to add a reference to the <code class="language-plaintext highlighter-rouge">Aspire.Hosting.SqlServer</code> package in your project. You can do this by running the following command in the terminal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet add package Aspire.Hosting.SqlServer
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Or add it through the NuGet Package Manager in Visual Studio or Rider.</p>

<p>Now, you should be able to run the application again, and the Aspire dashboard should show the <em>SqlServer</em> project as a running service. The first time you run the application, it will take a few moments build the container image for the SQL Server database, so be patient. Once the image is built, the application will start, and you should see the <em>SqlServer</em> project in the Aspire dashboard.  However, we are not done yet.  The database will be empty. So we are going to need to add a schema to it, and optionally, some seed data.</p>

<p>Run, the project again.  Make sure you have Docker or Podman running, and the SQL Server container will be created.</p>

<p>Depending on how quick you are at the looking at the Aspire dashboard, you may see the <em>SqlServer</em> as <em>unhealthy</em>, that is because the container/server is still being built.  Once the container is built, the <em>SqlServer</em> project will be marked as <em>healthy</em> and you can start using it.</p>

<p><img src="/assets/images/posts/2025/simplify-your-dot-net-development-with-aspire/aspire-dashboard-sql-server-unhealthy.png" alt="Aspire Dashboard - Sql Server Unhealthy" class="align-center" /></p>

<p>If you are using Sqlite as your database, you can add the following line to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file instead:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">sqlite</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">AddSqlite</span><span class="p">(</span><span class="s">"Sqlite"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This will require the <em>CommunityToolkit.Aspire.Hosting.Sqlite</em> package, which you can add by running the following command in the terminal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet add package CommunityToolkit.Aspire.Hosting.Sqlite
</pre></td></tr></tbody></table></code></pre></div></div>

<p>If you are using Sql Server, wait until the <code class="language-plaintext highlighter-rouge">SqlServer</code> project is marked as <em>healthy</em> in the Aspire dashboard before you continue. If you are using Sqlite, you can continue without waiting for the database to be created.</p>

<p>Stop the project if it is still running, and let’s add the database schema to the project.</p>

<h4 id="create-the-database-schema">Create the Database Schema</h4>

<p>To create the database schema for this sample, we add some code to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file in the <em>Contacts.AppHost</em> project that will run the database creation scripts that are part of this project.</p>

<p class="notice--info"><strong><em>Note</em></strong>: Having scripts to create the database schema not only is a best practice, but it also allows other developers to easily set up the database schema in their local development environment and not need a central “development” database server.</p>

<p>If you cloned this repository to follow along, you will find the database creation scripts in the <code class="language-plaintext highlighter-rouge">db-scripts</code> folder in the root of the project.  There is a folder for each database type, such as <code class="language-plaintext highlighter-rouge">sqlserver</code> and <code class="language-plaintext highlighter-rouge">sqlite</code>. Each folder contains two files <code class="language-plaintext highlighter-rouge">create-data.sql</code> and <code class="language-plaintext highlighter-rouge">create-tables.sql</code>. The <code class="language-plaintext highlighter-rouge">create-data.sql</code> file contains the SQL commands to insert seed data into the database, while the <code class="language-plaintext highlighter-rouge">create-tables.sql</code> file contains the SQL commands to create the database schema.  There is also an extra file for the SQL Server database called <code class="language-plaintext highlighter-rouge">create-database-and-user.sql</code>, which contains the SQL commands to create the database and user for the SQL Server database.</p>

<p>Back to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file in the <em>Contacts.AppHost</em> project. We are going to need to create the database based on these scripts. We will add the following code to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file, just after the <code class="language-plaintext highlighter-rouge">var sql</code> line for the database creation:</p>

<p>For SQL Server, add the following code:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">path</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">AppHostDirectory</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">sqlText</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Concat</span><span class="p">(</span>
    <span class="n">File</span><span class="p">.</span><span class="nf">ReadAllText</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="nf">Combine</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">@"..\..\db-scripts\sqlserver\create-database-and-user.sql"</span><span class="p">)),</span> 
    <span class="s">" "</span><span class="p">,</span>
    <span class="n">File</span><span class="p">.</span><span class="nf">ReadAllText</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="nf">Combine</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">@"..\..\db-scripts\sqlserver\create-tables.sql"</span><span class="p">)),</span>
    <span class="s">" "</span><span class="p">,</span>    
    <span class="n">File</span><span class="p">.</span><span class="nf">ReadAllText</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="nf">Combine</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">@"..\..\db-scripts\sqlserver\create-data.sql"</span><span class="p">)));</span>

<span class="kt">var</span> <span class="n">db</span> <span class="p">=</span> <span class="n">sql</span><span class="p">.</span><span class="nf">AddDatabase</span><span class="p">(</span><span class="s">"Contacts"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithCreationScript</span><span class="p">(</span><span class="n">sqlText</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>Note</em></strong>: For the SQL Server database, Aspire will report it <em>unhealthy</em> as the database is not created yet.  Once the database is created, it will be marked as <em>healthy</em>.</p>

<p class="notice--warning"><strong><em>Note</em></strong>: If you are using your own scripts, make sure that the scripts are in the correct order, and that each script ends with an new line/carriage return or an empty line. This is important as Aspire will execute the scripts in the order they are provided, and if there is no new line at the end of the script, it may not execute correctly.</p>

<p>For Sqlite, add the following code:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">path</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">AppHostDirectory</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">sqlText</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Concat</span><span class="p">(</span>
    <span class="n">File</span><span class="p">.</span><span class="nf">ReadAllText</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="nf">Combine</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">@"..\..\db-scripts\sqlite\create-tables.sql"</span><span class="p">)),</span>
    <span class="s">" "</span><span class="p">,</span>    
    <span class="n">File</span><span class="p">.</span><span class="nf">ReadAllText</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="nf">Combine</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">@"..\..\db-scripts\sqlite\create-data.sql"</span><span class="p">)));</span>

<span class="kt">var</span> <span class="n">db</span> <span class="p">=</span> <span class="n">sql</span><span class="p">.</span><span class="nf">AddDatabase</span><span class="p">(</span><span class="s">"Contacts"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithCreationScript</span><span class="p">(</span><span class="n">sqlText</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now when you run the application, Aspire will execute the database creation scripts and create the database schema. You can verify that the database was created by checking the Aspire dashboard, where you should see the <code class="language-plaintext highlighter-rouge">Contacts</code> database listed under the <code class="language-plaintext highlighter-rouge">SqlServer</code> or <code class="language-plaintext highlighter-rouge">Sqlite</code> project, depending on which database you are using.  At this point, you can even connect to the database using a database management tool to see the database schema and the seed data that was added.</p>

<h4 id="having-the-contactsapi-use-the-aspire-managed-database">Having the Contacts.Api Use the Aspire Managed Database</h4>

<p>Now object/project creation matters.  Since the <em>Contacts.Api</em> project depends on the database, we need to make sure that the <em>Contacts.Api</em> project is aware of the database that we just created.  We can do this by adding a reference to the database in the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file.  This is going to require us to move the <code class="language-plaintext highlighter-rouge">AddProject</code> line for the <em>Contacts.Api</em> project to just after the <code class="language-plaintext highlighter-rouge">var db</code> line, so that the database is created before the API project is added.  The <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file should look like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">DistributedApplication</span><span class="p">.</span><span class="nf">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>

<span class="c1">// Comments out the following 2 lines if you are using Sqlite</span>
<span class="kt">var</span> <span class="n">sql</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">AddSqlServer</span><span class="p">(</span><span class="s">"SqlServer"</span><span class="p">)</span>   
    <span class="p">.</span><span class="nf">WithLifetime</span><span class="p">(</span><span class="n">ContainerLifetime</span><span class="p">.</span><span class="n">Persistent</span><span class="p">);</span>

<span class="c1">// Uncomment out is using Sqlite</span>
<span class="c1">// var sql = builder.AddSqlite("Sqlite");</span>

<span class="kt">var</span> <span class="n">path</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">AppHostDirectory</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">sqlText</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Concat</span><span class="p">(</span>
    <span class="n">File</span><span class="p">.</span><span class="nf">ReadAllText</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="nf">Combine</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">@"..\..\db-scripts\sqlserver\create-database-and-user.sql"</span><span class="p">)),</span> 
    <span class="s">" "</span><span class="p">,</span>
    <span class="n">File</span><span class="p">.</span><span class="nf">ReadAllText</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="nf">Combine</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">@"..\..\db-scripts\sqlserver\create-tables.sql"</span><span class="p">)),</span>
    <span class="s">" "</span><span class="p">,</span>    
    <span class="n">File</span><span class="p">.</span><span class="nf">ReadAllText</span><span class="p">(</span><span class="n">Path</span><span class="p">.</span><span class="nf">Combine</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">@"..\..\db-scripts\sqlserver\create-data.sql"</span><span class="p">)));</span>

<span class="kt">var</span> <span class="n">db</span> <span class="p">=</span> <span class="n">sql</span><span class="p">.</span><span class="nf">AddDatabase</span><span class="p">(</span><span class="s">"Contacts"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithCreationScript</span><span class="p">(</span><span class="n">sqlText</span><span class="p">);</span>

<span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_Api</span><span class="p">&gt;(</span><span class="s">"contacts-api"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now we need to update the <em>Contacts.Api</em> configuration to use the Aspire managed database. This is done by chaining a call to the <code class="language-plaintext highlighter-rouge">WithEnvironment</code> method on the <code class="language-plaintext highlighter-rouge">AddProject</code> method for the <em>Contacts.Api</em> project. This will pass the database connection string to the API project, allowing it to connect to the database.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_Api</span><span class="p">&gt;(</span><span class="s">"contacts-api"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithEnvironment</span><span class="p">(</span><span class="s">"ConnectionStrings__ContactsDatabaseSqlServer"</span><span class="p">,</span> <span class="n">db</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WaitFor</span><span class="p">(</span><span class="n">db</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>or for Sqlite:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_Api</span><span class="p">&gt;(</span><span class="s">"contacts-api"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithEnvironment</span><span class="p">(</span><span class="s">"ConnectionStrings__ContactsDatabaseSqlite"</span><span class="p">,</span> <span class="n">db</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WaitFor</span><span class="p">(</span><span class="n">db</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>Note</em></strong>: Note the double underscore (<code class="language-plaintext highlighter-rouge">__</code>) in the environment variable name. This is a convention used by .NET to separate the sections of the configuration key. In this case, it separates the <code class="language-plaintext highlighter-rouge">Settings</code> section from the <code class="language-plaintext highlighter-rouge">ContactsDatabaseSqlServer</code> or <code class="language-plaintext highlighter-rouge">ContactsDatabaseSqlite</code> key. You can see the settings in the <code class="language-plaintext highlighter-rouge">appsettings.json</code> file in the <em>Contacts.Api</em> project, where the connection string is defined.</p>

<p>The <code class="language-plaintext highlighter-rouge">WaitFor()</code> command tells Aspire to wait for the database to be created before starting the <em>Contacts.Api</em> project. This is important because the API project needs to connect to the database, and if the database is not created yet, it will fail to start.</p>

<p>Run the application again, and you should see that the <em>Contacts.Api</em> project is now marked as <em>healthy</em> in the Aspire dashboard. You can also verify that the API is able to connect to the database by checking the logs in the Aspire dashboard.</p>

<h3 id="adding-the-web-app-project">Adding the Web App Project</h3>

<p>Now that we have the <em>Contacts.Api</em> project added to the Aspire application, let’s add the <em>Contacts.WebUi</em> project. This project is a simple ASP.NET Core web application that allows users to interact with the API.</p>

<p>As you might be able to guess, we will add the <em>Contacts.WebUi</em> project to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file in the <em>Contacts.AppHost</em> project.</p>

<p>Since the Web UI project depends on the API project, we need to make sure that the API project is created before the Web UI project, then we need to get a reference to it.</p>

<p>Go back to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file in the <em>Contacts.AppHost</em> project, find the line that adds the <em>Contacts.Api</em> project.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_Api</span><span class="p">&gt;(</span><span class="s">"contacts-api"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithEnvironment</span><span class="p">(</span><span class="s">"ConnectionStrings__ContactsDatabaseSqlServer"</span><span class="p">,</span> <span class="n">db</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Change this line to add a variable named <code class="language-plaintext highlighter-rouge">api</code> to hold the reference to the API project:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">api</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_Api</span><span class="p">&gt;(</span><span class="s">"contacts-api"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithEnvironment</span><span class="p">(</span><span class="s">"ConnectionStrings__ContactsDatabaseSqlServer"</span><span class="p">,</span> <span class="n">db</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now, we can add the <em>Contacts.WebUi</em> project to the Aspire application. Add the following line after the <em>Contacts.Api</em> project line:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_WebUi</span><span class="p">&gt;(</span><span class="s">"contacts-web-ui"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>However, we need to make sure that the Web UI project is aware of the API project, so we need to add a reference to the API project in the <em>Contacts.WebUi</em> project. We can do this by chaining a call to the <code class="language-plaintext highlighter-rouge">WithEnvironment</code> method on the <code class="language-plaintext highlighter-rouge">AddProject</code> method for the <em>Contacts.WebUi</em> project.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_WebUi</span><span class="p">&gt;(</span><span class="s">"contacts-web-ui"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithReference</span><span class="p">(</span><span class="n">api</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WaitFor</span><span class="p">(</span><span class="n">api</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This makes sure that the <em>ContactsApi</em> project starts before the <em>Contacts.WebUi</em> project, and that the Web UI project is aware of the API project. The <code class="language-plaintext highlighter-rouge">WithReference(api)</code> method tells Aspire to pass the API project reference to the Web UI project, allowing it to connect to the API. The <code class="language-plaintext highlighter-rouge">WaitFor(api)</code> method tells Aspire to wait for the API project to be created before starting the Web UI project.</p>

<p>Now this <em>Contacts.WebUi</em> project requires a Url to the API’s that <em>Contacts.Api</em> has.  There is a setting in the <code class="language-plaintext highlighter-rouge">appsettings.json</code> file in the <em>Contacts.WebUi</em> project that specifies the URL of the API. We can set this URL to the URL of the API project using a convention that Aspire provides.  Open up the <code class="language-plaintext highlighter-rouge">appsettings.Development.json</code> file in the <em>Contacts.WebUi</em> project, and find the <code class="language-plaintext highlighter-rouge">ApiRootUrl</code> setting. Then update the value to <code class="language-plaintext highlighter-rouge">https://contacts-api</code>. The <code class="language-plaintext highlighter-rouge">contacts-api</code> should match whatever name you used when you added <em>Contacts.Api</em> project. The Aspire framework will automatically resolve this to the URL of the API project when the application is run.</p>

<p>Now, when you run the application, Aspire will start the <em>Contacts.WebUi</em> project and pass the URL of the API project to it. You can verify that the Web UI project is able to connect to the API clicking on the <strong>Contacts</strong> line in the Web Ui project.</p>

<p>However, you may notice that the Web UI project is not able to connect to the API project. This is because the Web UI project is trying to connect to the API project using the URL <code class="language-plaintext highlighter-rouge">https://contacts-api</code>, but the <em>Contacts.WebUi</em> project does not know how to “connect” to that end point because we did not wire up some of the Aspire configuration.</p>

<p>Open up the <em>Contacts.WebUi</em> project, and open the <code class="language-plaintext highlighter-rouge">Program.cs</code> file.  Just after we create the <code class="language-plaintext highlighter-rouge">builder</code> variable, we need to add the following line:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">builder</span><span class="p">.</span><span class="nf">AddServiceDefaults</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then after the <code class="language-plaintext highlighter-rouge">var app = builder.Build()</code> line and before the <code class="language-plaintext highlighter-rouge">app.Run()</code> line, we need to add the following line:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">app</span><span class="p">.</span><span class="nf">MapDefaultEndpoints</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The last step is to add a reference to the <em>Contacts.ServiceDefaults</em> project in the <em>Contacts.WebUi</em> project. You can do this by running the following command in the terminal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet add .<span class="se">\C</span>ontacts.WebUi<span class="se">\C</span>ontacts.WebUi.csproj reference .<span class="se">\C</span>ontacts.ServiceDefaults<span class="se">\C</span>ontacts.ServiceDefaults.csproj
</pre></td></tr></tbody></table></code></pre></div></div>

<p>or by adding the reference through the NuGet Package Manager in Visual Studio or Rider.</p>

<p class="notice--info"><strong><em>Note</em></strong>: If you are using Visual Studio, the <code class="language-plaintext highlighter-rouge">AddServiceDefaults</code> and <code class="language-plaintext highlighter-rouge">MapDefaultEndpoints</code> methods are added automatically when you add the Aspire projects to the solution. If you are using Visual Studio Code or JetBrains Rider, you need to add these lines manually.</p>

<p>Now, when you run the application again, the Web UI project should be able to connect to the API project, and you should be able to see the contacts in the Web UI.</p>

<p><img src="/assets/images/posts/2025/simplify-your-dot-net-development-with-aspire/aspire-contacts-web-ui.png" alt="Contacts Web UI" class="align-center" /></p>

<p>At this point, everything looks good, and the application is running as expected. You can add, edit, delete, and list contacts using the Web UI, and the API is able to connect to the database. However, we still have one more step to complete before we are done. We need to add support for storing contact images in Azure Blob storage.</p>

<h3 id="adding-blob-storage-to-the-application">Adding Blob Storage to the Application</h3>

<p>The application requires Azure Blob storage to store uploaded contact images. The project <em>Contacts.ImageManager</em> is responsible for managing the images. It uses a library I wrote called <a href="https://github.com/jguadagno/JosephGuadagno.AzureHelpers.Storage" target="_blank">JosephGuadagno.AzureHelpers.Storage</a> to interact with Azure Blob storage. I know, an original name, right?  This library provides a set of helper methods to make it easier to work with Azure Blob storage in .NET applications.</p>

<p>The project <em>Contacts.WebUi</em> uses the <em>Contacts.ImageManager</em> project to upload the contact images to Azure Blob storage. There are two blobs containers that are used in this project:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">contact-images</code>: This container is used to store the contact images.</li>
  <li><code class="language-plaintext highlighter-rouge">contact-images-thumbnails</code>: This container is used to store the thumbnail images of the contact images.</li>
</ol>

<p>For local development, we will use the <a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite?WT.mc_id=DT-MVP-4024623" target="_blank">Azurite</a> emulator to simulate Azure Blob storage. Azurite is a lightweight server that simulates the Azure Blob storage service locally. It is useful for local development and testing purposes.</p>

<p>To add Azurite to the Aspire application, we will use the <code class="language-plaintext highlighter-rouge">Aspire.Hosting.AzureStorage</code> package to the <em>Contacts.AppHost</em> project. This package provides support for Azure Blob storage in Aspire applications. We are then going to need to update the application configuration to use the Aspire managed Azurite instance.</p>

<p>You can add the <code class="language-plaintext highlighter-rouge">Aspire.Hosting.Azure.Storage</code> package to the <em>Contacts.AppHost</em> project by running the following command in the terminal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>dotnet add package Aspire.Hosting.Azure.Storage
</pre></td></tr></tbody></table></code></pre></div></div>

<p>or by adding it through the NuGet Package Manager in Visual Studio or Rider.</p>

<p>Since the storage emulator is going to be used by the <em>Contacts.WebUi</em> project, we need to add the reference to the storage component to Aspire before we add the <em>Contacts.WebUi</em> project.  Open the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file in the <em>Contacts.AppHost</em> project, and add the following line after the <code class="language-plaintext highlighter-rouge">var db</code> line:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">blobStorage</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">AddAzureStorage</span><span class="p">(</span><span class="s">"AzureStorage"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">RunAsEmulator</span><span class="p">(</span><span class="n">azurite</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">azurite</span><span class="p">.</span><span class="nf">WithLifetime</span><span class="p">(</span><span class="n">ContainerLifetime</span><span class="p">.</span><span class="n">Persistent</span><span class="p">);</span>
    <span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This line adds the Azure Blob storage component to the Aspire application, with the name <code class="language-plaintext highlighter-rouge">AzureStorage</code>. The <code class="language-plaintext highlighter-rouge">RunAsEmulator()</code> method specifies that the Azurite emulator should be used instead of a real Azure Blob storage account. Just as we did with the SQL Server database, we are using the <code class="language-plaintext highlighter-rouge">WithLifetime(ContainerLifetime.Persistent)</code> method to specify that the Azurite emulator should be persistent, meaning it will not be recreated every time the application is run. This is helpful for development and testing purposes, as it allows you to keep the data in the Azurite emulator between runs.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">blobStorage</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">AddAzureStorage</span><span class="p">(</span><span class="s">"AzureStorage"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">RunAsEmulator</span><span class="p">(</span><span class="n">azurite</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">azurite</span><span class="p">.</span><span class="nf">WithLifetime</span><span class="p">(</span><span class="n">ContainerLifetime</span><span class="p">.</span><span class="n">Persistent</span><span class="p">);</span>
    <span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Next, we need to create the blob containers that will be used by the application.  We can do this by calling the <code class="language-plaintext highlighter-rouge">AddBlobContainer("contact-images")</code> and <code class="language-plaintext highlighter-rouge">AddBlobContainer("contact-images-thumbnails")</code> methods on the <code class="language-plaintext highlighter-rouge">blobStorage</code> variable. Add the following line to the <code class="language-plaintext highlighter-rouge">AppHost.cs</code> file after the <code class="language-plaintext highlighter-rouge">AddBlobs("cwjgcontacts")</code> line:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">imageContainer</span> <span class="p">=</span> <span class="n">blobStorage</span><span class="p">.</span><span class="nf">AddBlobContainer</span><span class="p">(</span><span class="s">"contact-images"</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">thumbnailContainer</span> <span class="p">=</span> <span class="n">blobStorage</span><span class="p">.</span><span class="nf">AddBlobContainer</span><span class="p">(</span><span class="s">"contact-images-thumbnails"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now, we need to update <code class="language-plaintext highlighter-rouge">builder.AddProject&lt;Projects.Contacts_WebUi&gt;("contacts-web-ui")</code> to pass the Azure Blob storage connection string to the <em>Contacts.WebUi</em> project. We can do this by chaining a call to the <code class="language-plaintext highlighter-rouge">WithEnvironment</code> method on the <code class="language-plaintext highlighter-rouge">AddProject</code> method for the <em>Contacts.WebUi</em> project.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="n">builder</span><span class="p">.</span><span class="n">AddProject</span><span class="p">&lt;</span><span class="n">Projects</span><span class="p">.</span><span class="n">Contacts_WebUi</span><span class="p">&gt;(</span><span class="s">"contacts-web-ui"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithReference</span><span class="p">(</span><span class="n">api</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WaitFor</span><span class="p">(</span><span class="n">api</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WaitFor</span><span class="p">(</span><span class="n">blobStorage</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithEnvironment</span><span class="p">(</span><span class="s">"Settings__ContactBlobStorageAccount"</span><span class="p">,</span> <span class="n">imageContainer</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">WithEnvironment</span><span class="p">(</span><span class="s">"Settings__ContactThumbnailBlobStorageAccountName"</span><span class="p">,</span> <span class="n">thumbnailContainer</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now, we need to make a few tweaks to the <em>Contacts.WebUi</em> project to make be able to programmatically set the <code class="language-plaintext highlighter-rouge">ContactImageUrl</code> setting in the application.  For some reason, the service discovery feature of Aspire does not wire up blob storage URLs automatically, so we need to do it manually.</p>

<p>Open the <code class="language-plaintext highlighter-rouge">Program.cs</code> file in the <em>Contacts.WebUi</em> project, and add the following line just after the <code class="language-plaintext highlighter-rouge">configuration.Bind("Settings", settings);</code> line in the <code class="language-plaintext highlighter-rouge">ConfigureServices</code> method:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>    <span class="k">if</span> <span class="p">(</span><span class="n">environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">endpoint</span> <span class="p">=</span> <span class="n">settings</span><span class="p">.</span><span class="n">ContactBlobStorageAccount</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="sc">';'</span><span class="p">).</span><span class="nf">First</span><span class="p">(</span><span class="n">x</span> <span class="p">=&gt;</span> <span class="n">x</span><span class="p">.</span><span class="nf">StartsWith</span><span class="p">(</span><span class="s">"BlobEndpoint"</span><span class="p">));</span>
        <span class="n">settings</span><span class="p">.</span><span class="n">ContactImageUrl</span> <span class="p">=</span>  <span class="n">endpoint</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="sc">'='</span><span class="p">)[</span><span class="m">1</span><span class="p">].</span><span class="nf">TrimEnd</span><span class="p">(</span><span class="sc">'/'</span><span class="p">)</span> <span class="p">+</span> <span class="s">"/"</span><span class="p">;</span>
    <span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This code checks if the application is running in development mode, and if so, it sets the <code class="language-plaintext highlighter-rouge">ContactImageUrl</code> setting to the Blob storage endpoint URL by parsing the connection string.</p>

<p>Another thing that we need to do, for this application to work, is to programmatically set the blob container public access level to PublicAccessType.Blob.  Replace the <code class="language-plaintext highlighter-rouge">services.TryAddScoped&lt;IImageStore&gt;</code> line with the following code:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="n">services</span><span class="p">.</span><span class="n">TryAddScoped</span><span class="p">&lt;</span><span class="n">IImageStore</span><span class="p">&gt;(</span><span class="n">_</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">environment</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">blob</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Blobs</span><span class="p">(</span><span class="n">settings</span><span class="p">.</span><span class="n">ContactBlobStorageAccount</span><span class="p">,</span> <span class="n">settings</span><span class="p">.</span><span class="n">ContactImageContainerName</span><span class="p">);</span>
        <span class="n">blob</span><span class="p">.</span><span class="n">BlobContainerClient</span><span class="p">.</span><span class="nf">SetAccessPolicy</span><span class="p">(</span><span class="n">PublicAccessType</span><span class="p">.</span><span class="n">Blob</span><span class="p">);</span>
        
        <span class="k">return</span> <span class="k">new</span> <span class="nf">ImageStore</span><span class="p">(</span><span class="n">blob</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="k">new</span> <span class="nf">ImageStore</span><span class="p">(</span>
        <span class="k">new</span> <span class="nf">Blobs</span><span class="p">(</span><span class="n">settings</span><span class="p">.</span><span class="n">ContactBlobStorageAccountName</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="n">settings</span><span class="p">.</span><span class="n">ContactImageContainerName</span><span class="p">));</span>

<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>Note</em></strong>: I’ll create two issues to get this added to the Aspire.Hosting.Azure.Storage package, so that we don’t have to do this manually in the future.</p>

<p>This code checks if the application is running in development mode, and if so, it sets the <a href="https://learn.microsoft.com/en-us/azure/storage/blobs/anonymous-read-access-configure?tabs=portal&amp;WT.mc_id=DT-MVP-4024623" target="_blank">public access level</a> of the blob container to <code class="language-plaintext highlighter-rouge">PublicAccessType.Blob</code>. This allows the images to be publicly accessible, which is necessary for the Web UI to display the images.</p>

<p>Now, you can upload an image for a contact, the image will be uploaded to the Azure Blob storage emulator, and the Web UI will display the image. If you have the Azure Storage Explorer installed, you can connect to the Azurite emulator and see the images that were uploaded.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>Wow! That was a lot of work, but we have successfully added Aspire to the Contacts application and configured it to use SQL Server, Azure Blob storage, and the Web UI. We also created the database schema and seed data for the application, and we were able to run the application with all the components working together.  Now, you can focus on building features for the application without worrying about the underlying infrastructure and configuration.</p>

<p>In a “real world” application, it shouldn’t take you this long to get started with Aspire. The goal of this post was to walk you through setting up and application to show you how to get started with Aspire, and how to add the necessary components to your application.</p>

<p>If you want follow along with me learning Aspire, you can watch the <a href="https://www.youtube.com/watch?v=ZKgUwDWCtic" target="_blank">AspiriFridays episode</a> where we added Aspire to the JosephGuadagno.NET Broadcasting application. You can also check out the <a href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview?WT.mc_id=DT-MVP-4024623" target="_blank">official documentation</a> for more information on how to get started with Aspire.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/en-us/dotnet/aspire/?WT.mc_id=DT-MVP-4024623" target="_blank">.NET Aspire Documentation</a></li>
  <li><a href="https://www.youtube.com/watch?v=ZKgUwDWCtic" target="_blank">YouTube - AspiriFridays (Joe Guadagno’s Broadcast site)</a></li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.AzureHelpers.Storage" target="_blank">JosephGuadagno.AzureHelpers.Storage</a></li>
  <li><a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azurite?WT.mc_id=DT-MVP-4024623" target="_blank">Azurite</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Aspire" /><category term=".NET" /><category term="dotnet" /><category term="Development" /><category term="ASP.NET" /><category term="Containers" /><summary type="html"><![CDATA[Learn how Aspire can streamline your .NET development process, making it easier to manage dependencies and enhance productivity]]></summary></entry><entry><title type="html">Using Certificates for Authentication with Microsoft Identity Library</title><link href="https://www.josephguadagno.net/2025/06/22/using-certificates-for-authentication-with-microsoft-identity-library" rel="alternate" type="text/html" title="Using Certificates for Authentication with Microsoft Identity Library" /><published>2025-06-22T17:50:00-07:00</published><updated>2025-06-22T17:50:00-07:00</updated><id>https://www.josephguadagno.net/2025/06/22/using-certificates-for-authentication-with-microsoft-identity-library</id><content type="html" xml:base="https://www.josephguadagno.net/2025/06/22/using-certificates-for-authentication-with-microsoft-identity-library"><![CDATA[<p>A few weeks ago, I was on <a href="https://www.youtube.com/watch?v=ZKgUwDWCtic" target="_blank">AspiriFridays</a> where <a href="https://bsky.app/profile/damianedwards.com" target="_blank">Damian Edward</a>, <a href="https://bsky.app/profile/davidfowl.com" target="_blank">David Fowler</a>, <a href="https://bsky.app/profile/maddymontaquila.net" target="_blank">Maddy Montaquila</a> and I were adding .NET <a href="https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview?WT.mc_id=DT-MVP-4024623" target="_blank">Aspire</a> to the <a href="https://github.com/jguadagno/jjgnet-broadcast/" target="_blank">JosephGuadagno.NET Broadcasting</a> application. At one point during the conversation, I brought up that I might need to renew the client id and secret’s for the application. Damian mentioned that Microsoft mandated using certificates for inter-application authentication. I explained that I tried it once before but I couldn’t get it to work.  Well this weekend I decided to give it another try and I was able to get it working. In this article, I’m going to show you how to use certificates for authentication with the Microsoft Identity Library in .NET applications.</p>

<p>For the application, I used the <a href="https://learn.microsoft.com/en-us/entra/identity-platform/?WT.mc_id=DT-MVP-4024623" target="_blank">Microsoft Identity Platform</a> and the <a href="https://learn.microsoft.com/en-us/entra/identity-platform/msal-overview?tabs=dotnet&amp;WT.mc_id=DT-MVP-4024623" target="_blank">Microsoft Authentication Library (MSAL)</a>. The application is built using ASP.NET Core, Azure, Swagger, and other components. The web application requires user authentication via Entra, and the API is secured using the Microsoft Identity Platform. If you want more information on how to secure an API using the Microsoft Identity Platform, check out my previous series called <a href="/2020/06/12/protecting-an-asp-net-core-api-with-microsoft-identity-platform" target="_blank">Protecting an ASP.NET Core API with Microsoft Identity Platform</a>.</p>

<p>Now the documentation for using certificates with the Microsoft Identity Platform is a bit sparse, so I had to piece together the information from various sources. In this article, I’m going to show you how to use certificates for authentication with the Microsoft Identity Library in .NET applications.</p>

<h2 id="certificate-generation">Certificate Generation</h2>

<p>There are two ways to generate certificates for use with the Microsoft Identity Platform. You can generate a certificate locally or you can use a third-party service to generate the certificate. In this article, I’m going to show you both methods. The local method is useful for development and testing purposes, while the third-party method is more suitable for production environments.</p>

<h3 id="local-certificate-generation">Local Certificate Generation</h3>

<p>If you don’t have access to the production certificate, which most won’t, and you do not want to purchase a certificate yourself, you can generate a self-signed certificate.  It’s a couple of PowerShell commands to create a self-signed certificate to use for development and testing purposes. Open a PowerShell window and run the following commands:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">New-SelfSignedCertificate</span><span class="w"> </span><span class="nt">-KeyFriendlyName</span><span class="w"> </span><span class="s2">"JosephGuadagno-Broadcasting"</span><span class="w"> </span><span class="nt">-DnsName</span><span class="w"> </span><span class="s2">"josephguadagno-broadcasting.net"</span><span class="w"> </span><span class="nt">-CertStoreLocation</span><span class="w"> </span><span class="s2">"Cert:\CurrentUser\My"</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Replace the <code class="language-plaintext highlighter-rouge">-DnsName</code> parameter with the domain name you want to use for the certificate, and the <code class="language-plaintext highlighter-rouge">-KeyFriendlyName</code> parameter with a friendly name for the certificate. This command will create a self-signed certificate and store it in the current user’s personal certificate store.</p>

<p>This will generate a local certificate.  This certificate needs to be exported and registered in the application registration for which you want to use it in the Azure portal.  To export the certificate, in Windows, open the <code class="language-plaintext highlighter-rouge">certmgr.msc</code> application, or <em>Manage User Certificates</em>.</p>

<ul>
  <li>Locate the certificate in the <code class="language-plaintext highlighter-rouge">Personal\Certificates</code> folder</li>
  <li>Open the certificate</li>
  <li>Click <em>Details</em></li>
  <li>Click <em>Copy to File…</em></li>
  <li>Click <em>Next</em></li>
  <li>Select <em>No</em></li>
  <li>Select <em>DER encoded binary</em></li>
  <li>Click <em>Next</em></li>
  <li>Select a file name and location</li>
  <li>Click <em>Next</em></li>
  <li>Click <em>Finish</em></li>
</ul>

<p>You should see a message that the export was successful. Click <em>OK</em> to close the message. You now have a certificate that you can use for development and testing purposes.</p>

<h4 id="configure-the-application-to-use-the-certificate">Configure the Application to Use the Certificate</h4>

<p>Now that you have the certificate registered in Azure, you need to configure your application to use the certificate for authentication. This is done by adding some configuration settings to the <code class="language-plaintext highlighter-rouge">appsettings.json</code> file in your application.  Assuming that you already have the Microsoft Identity Platform configured in your application, you need to add the <code class="language-plaintext highlighter-rouge">ClientCertificates</code> section to the configuration settings in the <code class="language-plaintext highlighter-rouge">appsettings.json</code> file:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"AzureAd"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"Instance"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://login.microsoftonline.com/"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"Domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"yourdomain.onmicrosoft.com"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"TenantId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"your-tenant-id"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ClientId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"your-client-id"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ClientCertificates"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"SourceType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"StoreWithDistinguishedName"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"CertificateStorePath"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CurrentUser/My"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"CertificateDistinguishedName"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>For the <code class="language-plaintext highlighter-rouge">CertificateDistinguishedName</code> settings, enter the distinguished name field of the self-signed certificate you created earlier prepended with the <code class="language-plaintext highlighter-rouge">CN=</code>. The <code class="language-plaintext highlighter-rouge">CN=</code> is a search by common name. The <code class="language-plaintext highlighter-rouge">-DnsName</code> parameter contains the common name you used. In the sample above, it was <code class="language-plaintext highlighter-rouge">josephguadagno-broadcasting.net</code>.  So the <code class="language-plaintext highlighter-rouge">CertificateDistinguishedName</code> would be <code class="language-plaintext highlighter-rouge">CN=josephguadagno-broadcasting.net</code>.</p>

<h3 id="register-the-certificate-in-azure">Register the Certificate in Azure</h3>

<p>Once you have the certificate exported, you need to register it in the Azure portal. This is done by uploading the certificate to the application registration in Application Registrations in the Azure portal.</p>

<p>Navigate to the Azure portal and the application registration for your application. In the left-hand menu…</p>

<ul>
  <li>Click on <em>Manage</em></li>
  <li>Click on <em>Certificates &amp; secrets</em></li>
  <li>Click on the <em>Certificates</em> tab</li>
  <li>Click on the <em>Upload certificate</em> button</li>
  <li>In the blade that appears, in the <em>Select a file</em> input field, click on the <em>folder</em> button
    <ul>
      <li>Select the certificate file you exported earlier and click <em>Add</em>. The certificate will now be registered in the application registration.</li>
    </ul>
  </li>
  <li>End a description for the certificate in the <em>Description</em> input field. <strong><em>Note</em></strong>: You will need this description later when you configure the application to use the certificate.</li>
  <li>Click <em>Add</em> to add the certificate to the application registration.</li>
</ul>

<p>Once done, you should see the certificate listed in the <em>Certificates</em> tab.</p>

<p><img src="/assets/images/posts/2025/using-certificates-for-authentication-with-microsoft-identity-library/certificate-msal-azure-local.jpeg" alt="Certificate in Azure Portal" class="align-center" /></p>

<p>You can now use this certificate to authenticate your application with the Microsoft Identity Platform.</p>

<h3 id="third-party-certificate-generation">Third-Party Certificate Generation</h3>

<p>If you want to use a third-party service to generate the certificate, you can use a service like <a href="https://letsencrypt.org/" target="_blank">Let’s Encrypt</a> or <a href="https://www.digicert.com/" target="_blank">DigiCert</a>. These services will generate a certificate for you and provide you with the necessary files to use in your application. The process for generating the certificate will vary depending on the service you choose, so refer to the documentation for the service you choose.</p>

<p>I’m going to assume that you have a certificate generated by a third-party service and you have the necessary files to use in your application. The process for using the certificate in your application is similar to the process for using a self-signed certificate except you will need to generate a <code class="language-plaintext highlighter-rouge">.pfx</code> file from the certificate files provided by the service and then upload the <code class="language-plaintext highlighter-rouge">.pfx</code> file to the application registration in the Azure portal.</p>

<p>Let’s say you have a certificate file name <code class="language-plaintext highlighter-rouge">josephguadagno_net.crt</code> and a private key file name <code class="language-plaintext highlighter-rouge">josephguadagno_net.key</code>. You can generate a <code class="language-plaintext highlighter-rouge">.pfx</code> file using the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>openssl pkcs12 <span class="nt">-export</span> <span class="nt">-out</span> josephguadagno_net.pfx <span class="nt">-inkey</span> josephguadagno_net.key <span class="nt">-in</span> josephguadagno_net.crt
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This command will generate a <code class="language-plaintext highlighter-rouge">.pfx</code> file named <code class="language-plaintext highlighter-rouge">josephguadagno_net.pfx</code> that you can use in your application. You will be prompted to enter a password for the <code class="language-plaintext highlighter-rouge">.pfx</code> file. Make sure to remember this password as you will need it later when you configure the application to use the certificate.  Your <code class="language-plaintext highlighter-rouge">inkey</code> may also be a <code class="language-plaintext highlighter-rouge">.pem</code> file, so you can use the same command with the <code class="language-plaintext highlighter-rouge">.pem</code> file instead of the <code class="language-plaintext highlighter-rouge">.key</code> file.</p>

<h4 id="upload-the-certificate-to-azure">Upload the Certificate to Azure</h4>

<p>Now that you have the <code class="language-plaintext highlighter-rouge">.pfx</code> file, you need to upload it to the application registration and Azure Key Vault in the Azure portal.</p>

<h5 id="upload-the-certificate-to-azure-key-vault">Upload the Certificate to Azure Key Vault</h5>

<p>First step is to upload the certificate to the Azure Key Vault. Choose the Key Vault you want to use and follow these steps:</p>

<ul>
  <li>In the left-hand menu, click on <em>Objects</em></li>
  <li>Click on <em>Certificates</em></li>
  <li>Click on the <em>Generate/Import</em> button</li>
  <li>Change the <strong>Method of Certificate Creation</strong> to <em>Import</em></li>
  <li>Enter a name for the certificate in the <em>Certificate Name</em> input field. <strong><em>Note</em></strong>: You will need this name later when you configure the application to use the certificate.</li>
  <li>For the <em>Upload certificate file</em> input field, click on the <em>folder</em> button
    <ul>
      <li>Select the <code class="language-plaintext highlighter-rouge">.pfx</code> file you generated earlier and click <em>Add</em>. The certificate will now be uploaded to the Key Vault.</li>
    </ul>
  </li>
  <li>Enter the password for the <code class="language-plaintext highlighter-rouge">.pfx</code> file in the <em>Password</em> input field. This is the password you entered when you generated the <code class="language-plaintext highlighter-rouge">.pfx</code> file.</li>
  <li>Click <em>Create</em> to create the certificate in the Key Vault.</li>
</ul>

<p>Once done, you should see the certificate listed in the <em>Certificates</em> tab.</p>

<p><img src="/assets/images/posts/2025/using-certificates-for-authentication-with-microsoft-identity-library/certificate-msal-azure-cert.jpeg" alt="Certificate in Azure Key Vault" class="align-center" /></p>

<h5 id="upload-the-certificate-to-the-application-registration">Upload the Certificate to the Application Registration</h5>

<p>Next, you need to upload the certificate to the application registration in the Azure portal. Navigate to the Azure portal and the application registration for your application. In the left-hand menu click on <em>Manage</em> and then click on <em>Certificates &amp; secrets</em>.  Now this is the same process as uploading a self-signed certificate except we will need to upload the <code class="language-plaintext highlighter-rouge">.crt</code> file that was used to generate the <code class="language-plaintext highlighter-rouge">.pfx</code> file.</p>

<ul>
  <li>In the blade that appears, in the <em>Select a file</em> input field, click on the <em>folder</em> button
    <ul>
      <li>Select the certificate file you exported earlier and click <em>Add</em>. The certificate will now be registered in the application registration.</li>
    </ul>
  </li>
  <li>End a description for the certificate in the <em>Description</em> input field. <strong><em>Note</em></strong>: You will need this description later when you configure the application to use the certificate.</li>
  <li>Click <em>Add</em> to add the certificate to the application registration.</li>
</ul>

<p>Now you should see the certificate listed in the <em>Certificates</em> tab.</p>

<h4 id="configure-azure-to-use-the-certificate">Configure Azure to Use the Certificate</h4>

<p>Now that you have uploaded the certificate to both the Azure Key Vault and the application registration, you need to configure your application to use the certificate for authentication. This typically involves updating your application’s configuration to include the certificate information.</p>

<p>Assuming that you already have the Microsoft Identity Platform configured in your application and are using an Azure Web App, you need to add the following configuration settings to the <code class="language-plaintext highlighter-rouge">appsettings.json</code> file in your application:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"AzureAd"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"Instance"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://login.microsoftonline.com/"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"Domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"yourdomain.onmicrosoft.com"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"TenantId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"your-tenant-id"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ClientId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"your-client-id"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ClientCertificates"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"SourceType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"KeyVault"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"KeyVaultUrl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"your-key-vault-name"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"KeyVaultCertificateName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"your-certificate-name"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>or in the <em>Environment Variables</em> for the Azure Web App:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre>  {
    "name": "AzureAd__ClientCertificates__0__KeyVaultCertificateName",
    "value": "CertificateName",
    "slotSetting": false
  },
  {
    "name": "AzureAd__ClientCertificates__0__KeyVaultUrl",
    "value": "https://<span class="nt">&lt;your_vault&gt;</span>.vault.azure.net/",
    "slotSetting": false
  },
  {
    "name": "AzureAd__ClientCertificates__0__SourceType",
    "value": "KeyVault",
    "slotSetting": false
  },
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Replace the <code class="language-plaintext highlighter-rouge">KeyVaultName</code> and <code class="language-plaintext highlighter-rouge">CertificateName</code> parameters with the name of the Key Vault and the name of the certificate you uploaded earlier. The <code class="language-plaintext highlighter-rouge">SourceType</code> is set to <code class="language-plaintext highlighter-rouge">KeyVault</code> to indicate that the certificate is stored in Azure Key Vault.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>Using certificates for authentication with the Microsoft Identity Library in .NET applications is a secure way to authenticate your application with the Microsoft Identity Platform. In this article, I showed you how to generate a self-signed certificate for development and testing purposes, use existing certificates, how to upload the certificate to Azure Key Vault and the application registration, and how to configure your application to use the certificate for authentication.</p>

<p>I hope that this article has been helpful in getting you started with using certificates for authentication with the Microsoft Identity Library in .NET applications and that Microsoft will continue to improve the documentation for using certificates with the Microsoft Identity Platform. If you have any questions or comments, feel free to reach out to me though any of the social media links to the left.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="/2022/06/03/enabling-user-authentication-in-swagger-using-microsoft-identity">Enabling user authentication in Swagger using Microsoft Identity</a></li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-registering-an-application">Microsoft Identity - Register an application</a></li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-configure-local-development">Microsoft Identity - Configure Local Development</a></li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-assigning-a-role">Microsoft Identity - Assigning a Role</a></li>
  <li><a href="https://www.youtube.com/watch?v=ZKgUwDWCtic">YouTube - AspiriFridays (Joe Guadagno’s Broadcast site)</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Entra" /><category term="Identity" /><category term="Microsoft Identity Platform" /><category term="Managed Identity" /><category term="MSAL" /><category term="Azure" /><summary type="html"><![CDATA[This article provides instructions on how to use certificates for authentication with the Microsoft Identity Library in .NET applications.]]></summary></entry><entry><title type="html">Working with dotnet 10 Without Mucking Up Your Machine</title><link href="https://www.josephguadagno.net/2025/03/27/working-with-dotnet-10-without-mucking-up-your-machine" rel="alternate" type="text/html" title="Working with dotnet 10 Without Mucking Up Your Machine" /><published>2025-03-27T11:57:00-07:00</published><updated>2025-03-27T11:57:00-07:00</updated><id>https://www.josephguadagno.net/2025/03/27/working-with-dotnet-10-without-mucking-up-your-machine</id><content type="html" xml:base="https://www.josephguadagno.net/2025/03/27/working-with-dotnet-10-without-mucking-up-your-machine"><![CDATA[<p>I’ve been wanting to start working with .NET 10, but I didn’t want yet another version of .NET on my laptop. Although, I could cleanup the clutter like in this post, <a href="/2022/06/15/clean-up-some-dot-net-clutter" target="_blank">Clean Up Some .NET Clutter</a>, I wanted to use a Development Container. This posts will show you how to work with dotnet 10 effectively using Development Containers without affecting your local machine setup.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>If you are not familiar with Development Containers, I recommend reading my previous article on <a href="/2022/12/10/getting-started-with-developer-containers" target="_blank">Getting Started with Developer Containers</a> to get a better understanding of what Development Containers are and how to use them.</p>

<p>The rest of this article assumes you have a basic understanding of Development Containers and how to create and use them.</p>

<h2 id="create-a-development-container">Create a Development Container</h2>

<p>Normally, you would use the Command Pallette in Visual Studio Code to create a new Development Container. However, I’m going to use a <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file to create a new Development Container because, as of the writing of this posts, there is not a .NET 10 image.</p>

<p>To start, create a folder on your machine, something like <code class="language-plaintext highlighter-rouge">dotnet10</code>. Within that folder create a <code class="language-plaintext highlighter-rouge">.devcontainer</code> folder.  Inside that folder, create a <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file with the following content:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
    </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"dockerfile"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./Dockerfile"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"context"</span><span class="p">:</span><span class="w"> </span><span class="s2">"."</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>This instructs Visual Studio Code to use the <code class="language-plaintext highlighter-rouge">Dockerfile</code> in the current folder to build the Development Container.  Let’s now create the <code class="language-plaintext highlighter-rouge">Dockerfile</code> with the following content:</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="c"># Use the most recent .NET LTS as the base image</span>
<span class="k">FROM</span><span class="s"> mcr.microsoft.com/devcontainers/dotnet:1-8.0</span>

<span class="c"># Install the current .NET STS release on top of that</span>
<span class="k">COPY</span><span class="s"> --from=mcr.microsoft.com/dotnet/sdk:9.0 /usr/share/dotnet /usr/share/dotnet</span>

<span class="c"># Finally install the most recent .NET 10.0 preview using the dotnet-install script</span>
<span class="k">COPY</span><span class="s"> --from=mcr.microsoft.com/dotnet/nightly/sdk:10.0.100-preview.2 /usr/share/dotnet /usr/share/dotnet</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This <code class="language-plaintext highlighter-rouge">Dockerfile</code> will add version 8, 9, and 10 of .NET to the Development Container. It starts with the based image of .NET 8, which is the most recent LTS version. It then adds .NET 9 on top of that. Finally, it adds .NET 10.0 Preview 2.</p>

<p>Using this <code class="language-plaintext highlighter-rouge">Dockerfile</code> will allow you to pull the current version of .NET 10.0 Preview 2. If you want the nightly build, you would just need to rebuild the container and the latest 10.0 Preview 2 will be installed.  For future versions, you would just need to change the version number in the <code class="language-plaintext highlighter-rouge">Dockerfile</code>.</p>

<p>Save the <code class="language-plaintext highlighter-rouge">Dockerfile</code> and <code class="language-plaintext highlighter-rouge">devcontainer.json</code> files.  Now, open the folder in Visual Studio Code.  You should see a prompt to reopen the folder in a Development Container.  Ignore that prompt for now.</p>

<h3 id="add-vs-code-extensions">Add VS Code Extensions</h3>

<p>Next, we will want to add some extensions to Visual Studio Code.  I recommend the following extensions to aid in development:</p>

<ul>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=stoplight.spectral&amp;WT.mc_id=DT-MVP-4024623" target="_blank">Stoplight Spectral Extension</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi&amp;WT.mc_id=DT-MVP-4024623" target="_blank">OpenAPI (Swagger) Editor Extension</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons&amp;WT.mc_id=DT-MVP-4024623" target="_blank">VS Code Icons</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client&amp;WT.mc_id=DT-MVP-4024623" target="_blank">REST Client</a></li>
</ul>

<p>To do this, edit the <code class="language-plaintext highlighter-rouge">.devcontainer/devcontainer.json</code> file to include the following:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
    </span><span class="nl">"customizations"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"vscode"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"extensions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
                </span><span class="s2">"ms-dotnettools.csdevkit"</span><span class="p">,</span><span class="w">
                </span><span class="s2">"ms-dotnettools.dotnet-interactive-vscode"</span><span class="p">,</span><span class="w">
                </span><span class="s2">"vscode-icons-team.vscode-icons"</span><span class="p">,</span><span class="w">
                </span><span class="s2">"humao.rest-client"</span><span class="p">,</span><span class="w">
                </span><span class="s2">"42Crunch.vscode-openapi"</span><span class="p">,</span><span class="w">
                </span><span class="s2">"stoplight.spectral"</span><span class="w">
            </span><span class="p">]</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="startup-script">Startup script</h3>

<p>Finally, we will want to add a startup script to the <code class="language-plaintext highlighter-rouge">.devcontainer</code> folder. I generally would create a <code class="language-plaintext highlighter-rouge">scripts</code> folder within the <code class="language-plaintext highlighter-rouge">.devcontainer</code> folder. This script will be run when the Development Container starts. Create a <code class="language-plaintext highlighter-rouge">scripts/postCreateCommand.sh</code> file with the following content:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="c">#!/bin/bash</span>

<span class="c"># Update the workloads</span>
<span class="nb">sudo </span>dotnet workload update

<span class="c"># EF Tools</span>
dotnet tool <span class="nb">install</span> <span class="nt">-g</span> dotnet-ef
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This will update all the workloads and install the Entity Framework tools. Save the file.  You can add any other commands you want to run when the Development Container starts.</p>

<p>Go back to the <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file and add the following:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
    </span><span class="nl">"postCreateCommand"</span><span class="p">:</span><span class="w"> </span><span class="s2">".devcontainer/scripts/postCreateCommand.sh"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Save the changes to the <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file.</p>

<h2 id="build-the-development-container">Build the Development Container</h2>

<p>Now that we have the <code class="language-plaintext highlighter-rouge">Dockerfile</code>, <code class="language-plaintext highlighter-rouge">devcontainer.json</code>, and the startup script, we can build the Development Container.  Open the Command Pallette in Visual Studio Code and type <strong>Dev Containers: Rebuild and Reopen in Container</strong>.  This will build the Development Container and open the folder in the Development Container.</p>

<p>Once the Development Container is built, you can start working with .NET 10.  You can create a new project, run tests, and do anything you would normally do with .NET from the Terminal in Visual Studio Code.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>Using Development Containers is a great way to work with different versions of .NET without cluttering up your local machine.  You can easily switch between versions by changing the <code class="language-plaintext highlighter-rouge">Dockerfile</code> and rebuilding the Development Container.  You can also add extensions and scripts to make your development environment more productive.</p>

<p>If you want to see a completed example, check out the <a href="https://github.com/jguadagno/dotnet10-devcontainer" target="_blank">dotnet10-devcontainer repository</a>.</p>

<p>If you want to customize the Terminal prompt, you can use the steps in this post, <a href="/2025/03/27/add-and-customize-oh-my-zsh-in-a-linux-development-container" target="_blank">Add and Customize Oh My Zsh in a Linux Development Container</a>.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://devblogs.microsoft.com/dotnet/dotnet-in-dev-container/?WT.mc_id=DT-MVP-4024623" target="_blank">.NET in a DevContainer</a></li>
  <li><a href="/2025/03/27/add-and-customize-oh-my-zsh-in-a-linux-development-container" target="_blank">Add and Customize Oh My Zsh in a Linux Development Container</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=stoplight.spectral&amp;WT.mc_id=DT-MVP-4024623" target="_blank">Stoplight Spectral Extension</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi&amp;WT.mc_id=DT-MVP-4024623" target="_blank">OpenAPI (Swagger) Editor Extension</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons&amp;WT.mc_id=DT-MVP-4024623" target="_blank">VS Code Icons</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client&amp;WT.mc_id=DT-MVP-4024623" target="_blank">REST Client</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Containers" /><category term="Docker" /><category term=".NET" /><category term="ASP.NET Core" /><summary type="html"><![CDATA[This article provides insights on how to work with dotnet 10 effectively using Development Containers without affecting your local machine setup.]]></summary></entry><entry><title type="html">Add and Customize Oh My Zsh in a Linux Development Container</title><link href="https://www.josephguadagno.net/2025/03/27/add-and-customize-oh-my-zsh-in-a-linux-development-container" rel="alternate" type="text/html" title="Add and Customize Oh My Zsh in a Linux Development Container" /><published>2025-03-27T08:20:00-07:00</published><updated>2025-03-27T08:20:00-07:00</updated><id>https://www.josephguadagno.net/2025/03/27/add-and-customize-oh-my-zsh-in-a-linux-development-container</id><content type="html" xml:base="https://www.josephguadagno.net/2025/03/27/add-and-customize-oh-my-zsh-in-a-linux-development-container"><![CDATA[<p>Lately, I’ve been creating <a href="https://containers.dev/?wt.mc_id=DT-MVP-4024623" target="_blank">Development Containers</a> for my projects to ensure that my development environment is consistent across different machines and to not “pollute” my primary machine with a bunch of dependencies. One of the things I find missing when I use Development Containers is a powerful shell experience. In this article, we will explore how to add and customize Oh My Zsh in a Linux Development Container for a better development experience.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>If you are not familiar with Development Containers, I recommend reading my previous article on <a href="/2022/12/10/getting-started-with-developer-containers" target="_blank">Getting Started with Developer Containers</a> to get a better understanding of what Development Containers are and how to use them.</p>

<p>The rest of this article assumes you have a basic understanding of Development Containers and how to create and use them.</p>

<h2 id="adding-oh-my-zsh-to-a-development-container">Adding Oh My Zsh to a Development Container</h2>

<p>Oh My Zsh is an open-source framework for managing Zsh configurations. It comes with a vast collection of themes and plugins that can enhance your shell experience. To add Oh My Zsh to your Development Container, you can add the feature called <a href="https://github.com/devcontainers/features/tree/main/src/common-utils" target="_blank">common-utils</a> from the <a href="https://containers.dev/features?wt.mc_id=DT-MVP-4024623" target="_blank">Development Containers Features</a> catalog. The <code class="language-plaintext highlighter-rouge">common-utils</code> feature will install Zsh, Oh My Zsh, and configure Zsh as the default shell in your Development Container.</p>

<p>Open your <code class="language-plaintext highlighter-rouge">.devcontainer/devcontainer.json</code> file and add the following configuration to the <code class="language-plaintext highlighter-rouge">features</code> section:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="s2">"</span><span class="s">features"</span><span class="err">:</span> <span class="pi">{</span>
    <span class="s2">"</span><span class="s">ghcr.io/devcontainers/features/common-utils"</span><span class="pi">:</span> <span class="pi">{</span>
        <span class="s2">"</span><span class="s">installZsh"</span><span class="pi">:</span> <span class="nv">true</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">installOhMyZsh"</span><span class="pi">:</span> <span class="nv">true</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">installOhMyZshConfig"</span><span class="pi">:</span> <span class="nv">true</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">configureZshAsDefaultShell"</span><span class="pi">:</span> <span class="nv">true</span>
    <span class="pi">}</span>
<span class="pi">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>If you want to add any custom plugins or themes, you can do so by adding the feature of <a href="https://github.com/devcontainers-extra/features/tree/main/src/zsh-plugins" target="_blank">zsh-plugins</a> to the <code class="language-plaintext highlighter-rouge">features</code> section of the <code class="language-plaintext highlighter-rouge">devcontainer.json</code>. In the example below, I am going to add the <a href="https://github.com/zsh-users/zsh-autosuggestions" target="_blank">zsh-autosuggestions</a> and the <a href="https://github.com/zsh-users/zsh-syntax-highlighting" target="_blank">zsh-syntax-highlighting</a> plugins.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="s2">"</span><span class="s">ghcr.io/devcontainers-extra/features/zsh-plugins:0"</span><span class="err">:</span> <span class="pi">{</span>
    <span class="s2">"</span><span class="s">plugins"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">zsh-autosuggestions</span><span class="nv"> </span><span class="s">zsh-syntax-highlighting"</span><span class="pi">,</span>
    <span class="s2">"</span><span class="s">omzPlugins"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://github.com/zsh-users/zsh-autosuggestions.git</span><span class="nv"> </span><span class="s">https://github.com/zsh-users/zsh-syntax-highlighting.git"</span>
<span class="pi">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>Line 2: in the example above, the <code class="language-plaintext highlighter-rouge">plugins</code> entry, specifies the plugins to be installed.</li>
  <li>Line 3: the <code class="language-plaintext highlighter-rouge">omzPlugins</code> entry, specifies the locations to download the plugins from.</li>
</ul>

<p>Your complete <code class="language-plaintext highlighter-rouge">feature</code> section should look something like this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="s2">"</span><span class="s">features"</span><span class="err">:</span> <span class="pi">{</span>
    <span class="s2">"</span><span class="s">ghcr.io/devcontainers/features/common-utils"</span><span class="pi">:</span> <span class="pi">{</span>
        <span class="s2">"</span><span class="s">installZsh"</span><span class="pi">:</span> <span class="nv">true</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">installOhMyZsh"</span><span class="pi">:</span> <span class="nv">true</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">installOhMyZshConfig"</span><span class="pi">:</span> <span class="nv">true</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">configureZshAsDefaultShell"</span><span class="pi">:</span> <span class="nv">true</span>
    <span class="pi">},</span>
    <span class="s2">"</span><span class="s">ghcr.io/devcontainers-extra/features/zsh-plugins:0"</span><span class="pi">:</span> <span class="pi">{</span>
        <span class="s2">"</span><span class="s">plugins"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">zsh-autosuggestions</span><span class="nv"> </span><span class="s">zsh-syntax-highlighting"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">omzPlugins"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://github.com/zsh-users/zsh-autosuggestions.git</span><span class="nv"> </span><span class="s">https://github.com/zsh-users/zsh-syntax-highlighting.git"</span>
    <span class="pi">}</span>
<span class="pi">}</span><span class="err">,</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="the-dotfiles">The dotfiles</h3>

<p>The power of Oh My Zsh comes from the ability to customize your shell experience. Let’s take a look at how you can customize your Development Container with Oh My Zsh.</p>

<h4 id="oh-my-zsh-configuration">Oh My Zsh Configuration</h4>

<p>Oh My Zsh comes with a default configuration file called <code class="language-plaintext highlighter-rouge">.zshrc</code>. You can customize your shell experience by adding your configurations to the <code class="language-plaintext highlighter-rouge">.zshrc</code> file. You can find the <code class="language-plaintext highlighter-rouge">.zshrc</code> file in the <code class="language-plaintext highlighter-rouge">~/.oh-my-zsh</code> directory in your Development Container. You can further customize the <code class="language-plaintext highlighter-rouge">.zshrc</code> file by using the various themes and plugins available with Oh My Zsh. If you already have a <code class="language-plaintext highlighter-rouge">.zshrc</code> file that you use on your local machine, you can use that. Or if you don’t, you can use the one that use which is hosted on GitHub. My <a href="https://github.com/jguadagno/dotnet10-devcontainer/blob/main/.devcontainer/dotfiles/.zshrc" target="_blank">zshrc</a> file is available on GitHub.</p>

<p>For this sample, you will want to create a directory called <code class="language-plaintext highlighter-rouge">dotfiles</code> in your <code class="language-plaintext highlighter-rouge">.devcontainer</code> directory and add the <code class="language-plaintext highlighter-rouge">.zshrc</code> file to it. We’ll copy the files in a few steps ahead.</p>

<h4 id="powerlevel10k-configuration">Powerlevel10k Configuration</h4>

<p>I use the <a href="https://github.com/romkatv/powerlevel10k" target="_blank">Powerlevel10k</a> theme which has its own configuration file, <code class="language-plaintext highlighter-rouge">p10k.zsh</code>. Just like the <code class="language-plaintext highlighter-rouge">.zshrc</code> file, you can customize this file to your needs. This file needs to be in the <code class="language-plaintext highlighter-rouge">~/</code> directory in your Development Container. If you do not have a <code class="language-plaintext highlighter-rouge">p10k.zsh</code> file, you can use the one I provide in my GitHub repository. My <a href="https://github.com/jguadagno/dotnet10-devcontainer/blob/main/.devcontainer/dotfiles/.p10k.zsh" target="_blank">.p10k.zsh</a> file is available on GitHub.</p>

<p>Just like the <code class="language-plaintext highlighter-rouge">.zshrc</code> file, you will want to create a directory called <code class="language-plaintext highlighter-rouge">dotfiles</code> in your <code class="language-plaintext highlighter-rouge">.devcontainer</code> directory and add the <code class="language-plaintext highlighter-rouge">.p10k.zsh</code> file to it for this script to work.</p>

<h3 id="copying-the-dotfiles">Copying the dotfiles</h3>

<p>The next step will require you to copy the dotfiles to the appropriate directories in your Development Container. For this, I create a script that we will run in the <code class="language-plaintext highlighter-rouge">postCreateCommand</code> section of the <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file.</p>

<p>Start by creating a script called <code class="language-plaintext highlighter-rouge">post-create.sh</code> in the <code class="language-plaintext highlighter-rouge">.devcontainer</code> directory. I created a <code class="language-plaintext highlighter-rouge">scripts</code> folder in the <code class="language-plaintext highlighter-rouge">.devcontainer</code> directory and added the <code class="language-plaintext highlighter-rouge">post-create.sh</code> file to it. The <code class="language-plaintext highlighter-rouge">post-create.sh</code> file will look like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="c">#!/bin/bash</span>

<span class="c">#######</span>
<span class="c"># Config ZSH</span>
<span class="c">#######</span>
<span class="c"># powerline fonts for zsh theme</span>
git clone https://github.com/powerline/fonts.git
<span class="nb">cd </span>fonts
./install.sh
<span class="nb">cd</span> .. <span class="o">&amp;&amp;</span> <span class="nb">rm</span> <span class="nt">-rf</span> fonts

<span class="c"># oh-my-zsh plugins</span>
zsh <span class="nt">-c</span> <span class="s1">'git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/themes/powerlevel10k'</span>
<span class="nb">cp</span> .devcontainer/dotfiles/.zshrc ~
<span class="nb">cp</span> .devcontainer/dotfiles/.p10k.zsh ~
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Let’s break down the script:</p>

<ul>
  <li>Line 7-10: Clones the fonts repository and installs the fonts, which helps with the Powerlevel10k theme.</li>
  <li>Line 13: Clones the Powerlevel10k theme to the <code class="language-plaintext highlighter-rouge">~/.oh-my-zsh/custom/themes</code> directory.</li>
  <li>Line 14-15: Copies the <code class="language-plaintext highlighter-rouge">.zshrc</code> and <code class="language-plaintext highlighter-rouge">.p10k.zsh</code> files to the <code class="language-plaintext highlighter-rouge">~</code> <em>home</em> directory.</li>
</ul>

<h3 id="running-the-post-createsh-script">Running the post-create.sh script</h3>

<p>To run the <code class="language-plaintext highlighter-rouge">post-create.sh</code> script, you need to add it to the <code class="language-plaintext highlighter-rouge">postCreateCommand</code> section of your <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file. Here’s how you can do that:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="s2">"</span><span class="s">postCreateCommand"</span><span class="err">:</span> <span class="s2">"</span><span class="s">.devcontainer/scripts/post-create.sh"</span><span class="err">,</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now this will run the <code class="language-plaintext highlighter-rouge">post-create.sh</code> script after the container is created. However, we are not done yet. We need to enable the fonts in the terminal. That’s next.</p>

<h3 id="enabling-the-fonts-in-the-vs-code-terminal">Enabling the Fonts in the VS Code Terminal</h3>

<p>To enable the fonts in the VS Code terminal while you are in the Development Container, you will need to update the <code class="language-plaintext highlighter-rouge">customizations</code> section of the <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file. Add the following configuration to the <code class="language-plaintext highlighter-rouge">customizations</code> section:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="s2">"</span><span class="s">customizations"</span><span class="err">:</span> <span class="pi">{</span>
    <span class="s2">"</span><span class="s">vscode"</span><span class="pi">:</span> <span class="pi">{</span>
    <span class="s2">"</span><span class="s">settings"</span><span class="pi">:</span> <span class="pi">{</span>
        <span class="s2">"</span><span class="s">editor.tabSize"</span><span class="pi">:</span> <span class="nv">4</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">terminal.integrated.fontFamily"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">MesloLGM</span><span class="nv"> </span><span class="s">Nerd</span><span class="nv"> </span><span class="s">Font"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">terminal.integrated.fontLigatures.enabled"</span><span class="pi">:</span> <span class="nv">true</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">terminal.integrated.defaultProfile.linux"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">zsh"</span><span class="pi">,</span>
        <span class="s2">"</span><span class="s">terminal.integrated.profiles.linux"</span><span class="pi">:</span> <span class="pi">{</span>
            <span class="s2">"</span><span class="s">zsh"</span><span class="pi">:</span> <span class="pi">{</span>
                <span class="s2">"</span><span class="s">path"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/bin/zsh"</span><span class="pi">,</span>
                <span class="s2">"</span><span class="s">icon"</span><span class="pi">:</span> <span class="s2">"</span><span class="s">terminal-ubuntu"</span>
            <span class="pi">}</span>
        <span class="pi">}</span>
    <span class="pi">}</span>
<span class="pi">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Let’s break down the configuration:</p>

<ul>
  <li>Line 5: Sets the font family to <code class="language-plaintext highlighter-rouge">MesloLGM Nerd Font</code>. You’ll want to use a Monospaced font that supports the ligatures characters. Usually any font with <code class="language-plaintext highlighter-rouge">Nerd Font</code> in the name will work.</li>
  <li>Line 6: Enables font ligatures. This allows the icons to be displayed correctly.</li>
  <li>Line 7: Sets the default profile for the terminal to <code class="language-plaintext highlighter-rouge">zsh</code>.</li>
  <li>Line 8-12: Configures the <code class="language-plaintext highlighter-rouge">zsh</code> profile for the terminal. The <code class="language-plaintext highlighter-rouge">path</code> entry specifies the path to the <code class="language-plaintext highlighter-rouge">zsh</code> binary, and the <code class="language-plaintext highlighter-rouge">icon</code> entry specifies the icon to use for the terminal.</li>
</ul>

<h3 id="restart-or-rebuild-the-development-container">Restart or Rebuild the Development Container</h3>

<p>At this point, you are ready to build/rebuild your Development Container. This can be done through the Command Palette in VS Code by selecting <strong>Dev Containers: Rebuild Container</strong>.</p>

<h2 id="the-final-result">The Final Result</h2>

<p>After rebuilding the Development Container, you should see the Powerlevel10k theme in the terminal. If you used my samples, the prompt and terminal will look like this:</p>

<p class="align-center"><img src="/assets/images/posts/2025/add-and-customize-oh-my-zsh-in-a-linux-development-container/terminal-window.png" alt="Customized Terminal in a Linux Development Container" /></p>

<p>You can now customize your shell experience by adding your configurations to the <code class="language-plaintext highlighter-rouge">.zshrc</code> and <code class="language-plaintext highlighter-rouge">.p10k.zsh</code> files. You can also explore the various themes and plugins available with Oh My Zsh to tailor your environment to your preferences. Just remember to copy the modified files from the container to the <code class="language-plaintext highlighter-rouge">dotfiles</code> and rebuild the container.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>In conclusion, customizing your Development Container with Oh My Zsh can significantly enhance your shell experience, making your development process more efficient and enjoyable. I encourage you to explore the various themes and plugins available with Oh My Zsh to tailor your environment to your preferences.</p>

<p>If you don’t want to go through all of the steps, you can take a look at this <a href="https://github.com/jguadagno/dotnet10-devcontainer" target="_blank">repository</a> for a complete setup.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="/2022/12/10/getting-started-with-developer-containers" target="_blank">Getting Started with Developer Containers</a></li>
  <li><a href="https://containers.dev/?wt.mc_id=DT-MVP-4024623" target="_blank">Development Containers Overview</a></li>
  <li><a href="https://containers.dev/templates?wt.mc_id=DT-MVP-4024623" target="_blank">Development Containers Template</a></li>
  <li><a href="https://containers.dev/features?wt.mc_id=DT-MVP-4024623" target="_blank">Development Containers Features</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers&amp;wt.mc_id=DT-MVP-4024623" target="_blank">Development Containers Visual Studio Code Extension</a></li>
  <li><a href="https://code.visualstudio.com/docs/devcontainers/create-dev-container?wt.mc_id=DT-MVP-4024623" target="_blank">Create a Development Container</a></li>
  <li><a href="https://ohmyz.sh/" target="_blank">Oh My Zsh</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Containers" /><category term="Docker" /><summary type="html"><![CDATA[This article will guide you through the process of adding and customizing Oh My Zsh in a Linux Development Container for a better development experience.]]></summary></entry><entry><title type="html">Backing Up Your Laptop Applications, Packages, and Configuration</title><link href="https://www.josephguadagno.net/2025/03/03/backing-up-your-laptop-applications-packages-and-configuration" rel="alternate" type="text/html" title="Backing Up Your Laptop Applications, Packages, and Configuration" /><published>2025-03-03T16:11:00-07:00</published><updated>2025-03-03T16:11:00-07:00</updated><id>https://www.josephguadagno.net/2025/03/03/backing-up-your-laptop-applications-packages-and-configuration</id><content type="html" xml:base="https://www.josephguadagno.net/2025/03/03/backing-up-your-laptop-applications-packages-and-configuration"><![CDATA[<p>I’ve been thinking about purchasing a new laptop for travel.  This got me thinking about the trauma of setting up a new laptop. In the past, I would spend some time on my existing laptop “writing down” all the applications, settings, and configurations for my current laptop.  This was a tedious process and I would always forget something.  I would then spend the next few weeks remembering what I had forgotten and making the changes.  This was a very inefficient process.</p>

<p>For a while now I have been using <a href="https://www.marticliment.com/unigetui/" target="_blank">UniGetUi</a> to install (almost) all of the applications on my laptop.  This has saved me a lot of time since UniGetUi can install and update applications from popular package managers like <a href="https://learn.microsoft.com/en-us/windows/package-manager/?WT.mc_id=DT-MVP-4024623" target="_blank">Winget</a>, <a href="https://scoop.sh/" target="_blank">Scoop</a>, <a href="https://chocolatey.org/" target="_blank">Chocolatey</a>, <a href="https://pypi.org/" target="_blank">Pip</a>, <a href="https://www.npmjs.com/" target="_blank">Npm</a>, <a href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-install?WT.mc_id=DT-MVP-4024623" target="_blank">.NET Tool</a> and <a href="https://www.powershellgallery.com/?WT.mc_id=DT-MVP-4024623" target="_blank">PowerShell Gallery</a>.  Recently, UniGetUi has added a feature that allows you to backup the list of installed applications/packages to a Json file.  This file can then be used to install all of your applications/packages on a new laptop.</p>

<p>The engineer in me was wondering how I could store this configuration, more so the applications and configuration from this laptop that I use regularly.  I figured I would create a repository on GitHub and store the configuration there.  I could then clone this repository to a new laptop and run the UniGetUi application to install all the applications.  This would save me a lot of time and hassle.  I could also use this repository to store all my settings and configurations.  This would make setting up a new laptop a breeze.  I could also share this repository with others.  This would allow others to easily install all the applications/packages and settings that I use.  This would be a huge time saver for them as well.</p>

<h2 id="what-you-will-need">What you will need</h2>

<ul>
  <li>UniGetUi installed on your laptop.  You can download it from <a href="https://www.marticliment.com/unigetui/" target="_blank">here</a>.</li>
  <li>A Git account with a service like GitHub, BitBucket, or GitLab</li>
  <li>PowerShell installed on your laptop. PowerShell is optional, but the script I wrote to refresh the files is written in PowerShell. More on this later.</li>
</ul>

<h2 id="the-repository">The Repository</h2>

<p>I started by creating a repository on GitHub.  This particular repository is called <a href="https://github.com/jguadagno/machine-dell5570-config" target="_blank">machine-dell5570-config</a>. Initially, I added some of the common files I need for my configuration, like:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">.gitignore</code> - This a common <code class="language-plaintext highlighter-rouge">.gitignore</code> file that I use across my projects. It also serves a second purpose to make sure I don’t commit any secrets to the repository.</li>
  <li><code class="language-plaintext highlighter-rouge">Microsoft.PowerShell_profile.ps1</code> - This file is used to store my PowerShell profile.</li>
  <li><code class="language-plaintext highlighter-rouge">DESKTOP-8IKVGQI installed packages 2025-01-26 07-51-10.ubundle</code> - This file is used to store the applications/packages installed using UniGetUi.</li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">DESKTOP-8IKVGQI installed packages 2025-01-26 07-51-10.ubundle</code> file is the file that UniGetUi creates as a backup of your installed files. Underneath the hood, it is a json file that describes each application, the version, and the installation source. I can then use this file to install all the applications on a new laptop. However, UniGetUi will generate a new file each time it creates a backup. This would have created a lot of files in the repository and defeat the purpose of using Git. I also wanted to automate this process a little bit. I ended up creating a new PowerShell script to manage this.</p>

<h2 id="backing-up-the-applicationpackage-list-with-unigetui">Backing up the Application/Package List with UniGetUi</h2>

<p>To back up the application/package lists using UniGetUi, you can utilize the backup feature. This will create a JSON file containing all the installed applications/packages that were installed using UniGetUi, which can be helpful when transferring settings to a new machine. Here’s how to do it:</p>

<ol>
  <li>Open UniGetUi.</li>
  <li>Navigate to the settings menu.</li>
  <li>Scroll down to <em>Backup installed packages</em> and expand the section.</li>
</ol>

<p><img src="/assets/images/posts/2025/backing-up-your-laptop-configuration/unigetui-backup-settings.png" alt="UniGetUi Backup Settings" class="align-center" /></p>

<p class="notice--info">Note: This image was taken from version 3.1.7 of the application. The layout may vary slightly in other versions.</p>

<p>At this point you can configure the backup settings to your needs.  I’ve accepted the defaults which enabled a backup periodically, adds a timestamp to the filename, and saves the file to the <em>My Documents</em><code class="language-plaintext highlighter-rouge">\UniGetUi</code> folder.</p>

<p>As I mentioned in the previous section, this will generate a new file each time it creates a backup.  The naming convention seems to be <code class="language-plaintext highlighter-rouge">&lt;COMPUTERNAME&gt; installed packages YYYY-MM-DD HH-MM-SS.ubundle</code>.  I did not want a Git repository full of these files, so I created a PowerShell script to help me manage this. More on that next.</p>

<h2 id="the-powershell-script">The PowerShell Script</h2>

<p>As I mentioned before, the engineer wanted to automate this process a little bit.  I created a PowerShell script to automatically copy the latest files I care about to the git repository and commit them. Let’s walk through the script.</p>

<h3 id="unigetui-backup-file-management">UniGetUi Backup File Management</h3>

<p>To manage the UniGetUi backup files, I wrote a PowerShell script that automatically renames the latest backup file to a more manageable name and moves it to a designated folder where the git repository resides.</p>

<p>Let’s look at the code:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="c"># Initial common vairables</span><span class="w">
</span><span class="nv">$MyDocumentsFolder</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">Environment</span><span class="p">]::</span><span class="n">GetFolderPath</span><span class="p">(</span><span class="s1">'MyDocuments'</span><span class="p">)</span><span class="w">

</span><span class="c"># Get the latest UniGet Bundle</span><span class="w">
</span><span class="nv">$UniGetFolder</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$MyDocumentsFolder</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s2">"\UniGetUi"</span><span class="w">
</span><span class="nv">$UniGetBackupName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"UniGet Installed Packages.json"</span><span class="w">
</span><span class="nv">$NewestFile</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-ChildItem</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nv">$UniGetFolder</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s2">"*.ubundle"</span><span class="w"> </span><span class="nt">-File</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Sort-Object</span><span class="w"> </span><span class="nx">LastWriteTime</span><span class="w"> </span><span class="nt">-Descending</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Select-Object</span><span class="w"> </span><span class="nt">-First</span><span class="w"> </span><span class="nx">1</span><span class="w">
</span><span class="n">Copy-Item</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="nv">$NewestFile</span><span class="o">.</span><span class="nf">FullName</span><span class="w"> </span><span class="nt">-Destination</span><span class="w"> </span><span class="nv">$UniGetBackupName</span><span class="w">
</span><span class="n">git</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="nv">$UniGetBackupName</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>Line #2, I get the <code class="language-plaintext highlighter-rouge">My Documents</code> folder.</li>
  <li>Line #5, I create a variable to store the path to the UniGetUi folder.</li>
  <li>Line #6, I create a variable to store the name of the file I want to use.</li>
  <li>Line #7, I get the newest file in the UniGetUi folder.</li>
  <li>Line #8, I copy the newest file to the new name.</li>
  <li>Line #9, I add the new file to the git repository.</li>
</ul>

<h3 id="getting-the-latest-powershell-profile">Getting the latest PowerShell Profile</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="c"># Get the latest PowerShell_profile</span><span class="w">
</span><span class="n">Copy-Item</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="bp">$PROFILE</span><span class="w">
</span><span class="n">git</span><span class="w"> </span><span class="nx">add</span><span class="w"> </span><span class="o">.</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>Line #2, I copy the latest PowerShell profile to the git repository. <strong><em>Note:</em></strong> <code class="language-plaintext highlighter-rouge">$PROFILE</code> is a built-in variable in PowerShell that points to the current user’s profile script.</li>
  <li>Line #3, I add the new file to the git repository.</li>
</ul>

<h3 id="committing-the-changes">Committing the Changes</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="c"># Commit the updated files to git</span><span class="w">
</span><span class="nv">$gitMessage</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Updated files  </span><span class="si">$(</span><span class="n">Get-Date</span><span class="p">)</span><span class="s2">"
git commit -m </span><span class="nv">$gitMessage</span><span class="s2">
git push
</span></pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>Line #2, I create a variable to store the commit message, which includes the current date and time <code class="language-plaintext highlighter-rouge">$(Get-Date)</code>.</li>
  <li>Line #3, I commit the changes to the git repository with the commit message.</li>
  <li>Line #4, I push the changes to the remote repository.</li>
</ul>

<h3 id="the-full-script">The Full Script</h3>

<p>Having these tasks in a single script makes it easier to manage the files, add additional items, and commit them to the repository for safekeeping.</p>

<p>The full source for the PowerShell script can be found at <a href="https://github.com/jguadagno/machine-dell5570-config/blob/main/Refresh-Files.ps1" target="_blank">Refresh-Files.ps1</a>.</p>

<h2 id="restore-the-applications-and-packages">Restore the Applications and Packages</h2>

<p>To restore your applications and packages after setting up a new laptop, you can utilize the configuration files stored in your Git repository. Here’s a step-by-step guide:</p>

<ul>
  <li><strong>Install UniGetUi</strong>: Start by installing UniGetUi on your new laptop. You can download it from <a href="https://www.marticliment.com/unigetui/" target="_blank">here</a>.</li>
  <li><strong>Install Git</strong>: If you haven’t already, install Git on your new laptop. You can download it from <a href="https://git-scm.com/" target="_blank">here</a>.</li>
  <li><strong>Open a PowerShell Terminal</strong>: Open a PowerShell terminal on your new laptop.</li>
  <li><strong>Clone the Repository</strong>: Start by cloning your configuration repository to your new laptop using the following command:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>git clone &lt;Url Repository Url&gt; <span class="c"># Similar to https://github.com/jguadagno/machine-dell5570-config</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li><strong>Navigate to the Directory</strong>: Change into the cloned repository’s directory:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nb">cd</span> &lt;Path to the cloned repository&gt; <span class="c"># Example: cd machine-dell5570-config</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li><strong>Install Applications with UniGetUi</strong>: Use the JSON file previously created to install the applications.</li>
</ul>

<ol>
  <li>Open UniGetUi</li>
  <li>Navigate to the <em>Package Bundles</em></li>
  <li>Click <em>Open Existing Bundle</em></li>
  <li>Select the file <code class="language-plaintext highlighter-rouge">UniGet Installed Packages.json</code> from the cloned repository.</li>
  <li>Select all of the applications/packages you want to install.</li>
  <li>Click the <em>Install selection</em> button.</li>
</ol>

<ul>
  <li><strong>Restore PowerShell Profile</strong>: Copy the <code class="language-plaintext highlighter-rouge">Microsoft.PowerShell_profile.ps1</code> file back to your PowerShell profile location. You can do this using:</li>
</ul>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Copy-Item</span><span class="w"> </span><span class="nt">-Path</span><span class="w"> </span><span class="o">.</span><span class="nx">\Microsoft.PowerShell_profile.ps1</span><span class="w"> </span><span class="nt">-Destination</span><span class="w"> </span><span class="bp">$PROFILE</span><span class="w"> </span><span class="nt">-Force</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>At this point, you should have successfully restored your applications and settings to your new laptop. This process can save you a lot of time and effort when setting up a new machine.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>In conclusion, using UniGetUi along with a properly managed Git repository will greatly streamline the process of setting up a new laptop. Remember to regularly commit changes to keep your repository updated and ready for use on a new machine.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://www.marticliment.com/unigetui/" target="_blank">UniGetUi</a></li>
  <li><a href="https://learn.microsoft.com/en-us/windows/package-manager/?WT.mc_id=DT-MVP-4024623" target="_blank">Winget</a></li>
  <li><a href="https://scoop.sh/" target="_blank">Scoop</a></li>
  <li><a href="https://chocolatey.org/" target="_blank">Chocolatey</a></li>
  <li><a href="https://pypi.org/" target="_blank">Pip</a></li>
  <li><a href="https://www.npmjs.com/" target="_blank">Npm</a></li>
  <li><a href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-install?WT.mc_id=DT-MVP-4024623" target="_blank">.NET Tool</a></li>
  <li><a href="https://www.powershellgallery.com/?WT.mc_id=DT-MVP-4024623" target="_blank">PowerShell Gallery</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Technology" /><category term="git" /><category term="GitHub" /><category term="Settings" /><summary type="html"><![CDATA[I've been thinking about purchasing a new laptop for travel. This got me thinking about the trauma of setting up a new laptop. In the past, I would spend some time on my existing laptop 'writing down' all the applications, settings, and configurations for my current laptop. This was a tedious process and I would always forget something. I would then spend the next few weeks remembering what I had forgotten and making the changes. This was a very inefficient process.]]></summary></entry><entry><title type="html">My Presentation and Travel Equipment</title><link href="https://www.josephguadagno.net/2024/12/15/my-presentation-and-travel-equipment" rel="alternate" type="text/html" title="My Presentation and Travel Equipment" /><published>2024-12-15T18:54:00-07:00</published><updated>2025-02-25T17:11:00-07:00</updated><id>https://www.josephguadagno.net/2024/12/15/my-presentation-and-travel-equipment</id><content type="html" xml:base="https://www.josephguadagno.net/2024/12/15/my-presentation-and-travel-equipment"><![CDATA[<p>This month in a chat with the speakers of <a href="https://www.sharepointeurope.com/" target="_blank">ESPC</a> one of the United States based speakers asked about some travel gear recommendations. I shared my travel gear with them and they found it helpful. I thought it would be a good idea to share it with you all. I have been using this gear for a while now and I am very happy with it. I hope you find it helpful too.</p>

<h2 id="presentation-equipment">Presentation Equipment</h2>

<p>There are a few things that I always carry with me when I am presenting at a conference or a user group meeting.</p>

<h3 id="presentation-clicker">Presentation Clicker</h3>

<p><em>Presentation Clicker Air Mouse Function Wireless Presenter USB Powerpoint Clicker (only USB Receiver) for Laptop, Computer, Projector, Mac, PC</em> (<a href="https://amzn.to/3DerKxb" target="_blank">Amazon Link</a>). This device has more features than just advance and reverse slides. It has a built-in laser pointer, air mouse function, media play, and more. It is very handy when you are presenting and you need to move around the room.</p>

<h3 id="extra-monitor">Extra Monitor</h3>

<p>A lot of community conferences do not have “confidence monitors” for the speakers. I always carry a portable monitor with me. It is very handy when you are presenting and you need to see your slides. I use the <em>Lepow Portable Monitor, 15.6 Inch Full HD 1080P USB Type-C Computer Display</em> (<a href="https://amzn.to/41AnRwP" target="_blank">Amazon Link</a>). It’s lightweight and easy to carry. Plus I can use it as a second monitor when I am working from hotels.</p>

<p>Having a second monitor requires having an adapter to connect it to your laptop. The are two different cables to use with this monitor depending on the version you have.  I have an older version that supports USB-C and mini HDMI. For this, I have this cable: <em>Cable Matters High-Speed 4K Mini HDMI to HDMI Cable 15ft</em> (<a href="https://amzn.to/3ZvrnFO" target="_blank">Amazon Link</a>). If you are purchasing the model linked above, you will need a USB-C to USB-C cable. I recommend the <em>JSAUX 100W USB C to USB C Cable</em> (<a href="https://amzn.to/3DkujxN" target="_blank">Amazon Link</a>). I keep a couple of these cables in my bag for data transfer and charging.</p>

<h3 id="presentation-adapters">Presentation Adapters</h3>

<p>I never know what type of equipment I am going to be presenting on so I carry along a few different video “adapters” to accommodate different types of projectors. I use the USB C to HDMI Adapter, <em>Tuwejia USB 3.1 Type C Thunderbolt 3 to HDMI 4K Adapter Compatible with MacBook Pro 2018/2017, MacBook Air, Samsung Galaxy S10 S9 S8 Note 9, Surface Book 2 and More</em> (<a href="https://amzn.to/4glrEmg" target="_blank">Amazon Link</a>).</p>

<p>I also carry in my bag the <em>Anker 332 USB-C Hub (5-in-1) with 4K HDMI Display, 5Gbps - and 2 5Gbps USB-A Data Ports</em> (<a href="https://amzn.to/41TUaaf" target="_blank">Amazon Link</a>). This is a great adapter to have in your bag. It has a USB-C port, 2 USB-A ports, and an HDMI port. It is very handy when you need to connect multiple devices to your laptop or even power your USC-C laptop.</p>

<h4 id="presentation-cables">Presentation Cables</h4>

<p>I always have a few different cables in bag:</p>

<ul>
  <li><a href="https://amzn.to/49Cf9zS" target="_blank">Amazon Basics High-Speed 4k Ultra HD HDMI 2.0 Cable</a></li>
  <li><a href="https://amzn.to/3DkujxN" target="_blank">JSAUX 100W USB C to USB C Cable</a></li>
</ul>

<h3 id="cable-bag">Cable Bag</h3>

<p>This item took me a while to find the “right” one. I wanted something that was small enough to fit in my backpack but big enough to hold all of my cables. I found the <em>BUBM Double Layer Electronic Accessories Organizer, Travel Gadget Bag for Cables, USB Flash Drive, Plug and More</em> (<a href="https://amzn.to/4iFPkmD" target="_blank">Amazon Link</a>). It is perfect for my needs. It has a lot of pockets and compartments to hold all of my cables and adapters.</p>

<h2 id="travel-equipment">Travel Equipment</h2>

<p>For travel, I have a few different items that I always carry with me. Here is a list of the equipment that I carry with me.</p>

<p>I need something to carry all of my equipment in. I use the <em>TUMI - Alpha Bravo Search Backpack - Laptop Backpack for Men &amp; Women - Backpack for Work &amp; Travel - 17.0” X 14.0” X 8.3” - Navy Blue</em> (<a href="https://amzn.to/3P0ia3H" target="_blank">Amazon Link</a>), which my wife purchased for me for Christmas a few years ago. It is very handy when you are traveling. It has a lot of pockets and compartments to hold all of my equipment. It is very handy when you are traveling and you need to carry all of your equipment.</p>

<p>This charger is very handy when you are traveling. It has 4 USB-C ports, two USB-A and two USB-C, and can charge multiple devices at the same time. <em>USB C Wall Charger, ITEHIL 100W 4-Port Fast PD Charger, Portable Type C Charger with Foldable Plug</em> ( <a href="https://amzn.to/49AtQDP" target="_blank">Amazon Link</a>). It is very handy when you are traveling and you need to charge your laptop, phone, and other devices.</p>

<p>This charger is very handy when you are traveling for the night time. The <em>Wireless Charging Station for Samsung, 3 in 1 Foldable Wireless Charger for Samsung Galaxy S24 Ultra/S24/S23 Ultra/S22/Note 20/iPhone 16 Pro Max, Galaxy Watch Ultra/7/6/5/4/Buds(Travel Essentials)</em> (<a href="https://amzn.to/3ZTr49f" target="_blank">Amazon Link</a>). It has 3 charging pads, one for your phone, one for your watch, and one for your earbuds. I set it up in my hotel room when I get there to charge my phone, watch, and ear buds.</p>

<p>Since public speaking generally requires travel and you can not guarantee the quality of the power outlets on airplanes, trains, or at the venue, I always carry a portable charger with me. I use the <em>25800mAh Power Bank, 22.5w Fast Charging Battery Pack</em> (<a href="https://amzn.to/49HvRhC" target="_blank">Amazon Link</a>). This charger is very handy when you are traveling. It has a 25800mAh battery capacity and can charge multiple devices at the same time. It is very handy when you are traveling and you need to charge your laptop, phone, and other devices.</p>

<p><strong><em>Update</em></strong>: I’ve updated my portable charger to <em>Portable Charger 36800mAh,4 Outputs Power Bank, Dual Input 5V/3A External Battery Pack,USB-C in&amp;Out High-Speed Charging Backup Charger Compatible with iPhone 16/15/14/13,Samsung S23 Android Phone etc</em> (<a href="https://amzn.to/4bhj2uW" target="_blank">Amazon Link</a>). This charger has a 36800mAh battery capacity and can charge multiple devices at the same time. It is very handy when you are traveling and you need to charge your laptop, phone, and other devices. It also has a USB-C port that can charge your laptop.</p>

<p>Recently, I added trackers to all of my bags, including my laptop bag.  Since I have an Android based phone, I added the <em>Pebblebee Tracker Clip Universal - Rechargable Item Tracker</em> (<a href="https://amzn.to/3ZVfNVW" target="_blank">Amazon Link</a>). This product works both with Android and iOS devices. It came in very handy on the return flight from Stockholm when my bags decided to extend their vacation and not come home with me. :smile: Pebblebee also has a card version of the tracker that I have in my wallet and large suitcases. The <em>Pebblebee Tracker Card Universal - Rechargeable Slim Item Tracker</em> (<a href="https://amzn.to/4gdMDHm" target="_blank">Amazon Link</a>).</p>

<h2 id="international-travel-equipment">International Travel Equipment</h2>

<p>For international travel, I have a few different items that I always carry with me. Here is a list of the equipment that I carry with me.</p>

<p>This adapter is very handy when you are traveling internationally. The <em>Universal Travel Adapter, VINTAR 75W GaN International Power Adapter with 3 USB C PD and 2 USB A QC, All in One Travel Essentials for UK/EU/USA/AUS</em> (<a href="https://amzn.to/41zVq1O" target="_blank">Amazon Link</a>). It has 3 USB-C ports, 2 USB-A ports, and can charge multiple devices at the same time. It is very handy when you are traveling and you need to charge your laptop, phone, and other devices. It also has a built-in power converter so you can use it in different countries. In addition, it supports fast charging, so you can charge your devices quickly.</p>

<p>I primarily use a Dell laptop and it’s AC adapter has a “<a href="https://en.wikipedia.org/wiki/IEC_60320" target="_blank">mickey mouse</a>” style plug, or more technically known as an IEC 60320 C5 connector, to power the power brick. This makes it easy to find a replacement power cord when traveling internationally. I use the <em>CableLeader 6ft Right Angle European 3-Prong Notebook Power Cord CEE 7/7</em> (<a href="https://amzn.to/3OX0XrW" target="_blank">Amazon Link</a>). For the United Kingdom I use <em>UK 3 Prong AC Power Cord Cable for Dell HP Asus Sony Toshiba Lenovo Acer Charger (UK Power Cord)</em> (<a href="https://amzn.to/41c3QuF{:target=&quot;_blank&quot;}">Amazon Link</a>) .These power cords are very handy when you are traveling internationally. They both have a right-angle plug that makes it easy to plug into the wall outlet. It is very handy when you are traveling and you need to charge your laptop.</p>

<h2 id="bonus-equipment">Bonus Equipment</h2>

<p>For the wife, the <em>Simran 1875 Watts International Travel Voltage Converter For Hair Dryer, Hair Straightener, Flat Iron, Curling Iron, Hair Curlers, travel Power Converter 220v to 110v, travel Adapter US to UK Europe AU</em> (<a href="https://amzn.to/4iAicg9" target="_blank">Amazon Link</a>). This converter is very handy when you are traveling internationally. It has a 1875 Watts power capacity and can convert 220v to 110v. It is very handy when you are traveling and you need to use your hair dryer, hair straightener, flat iron, curling iron, or hair curlers.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>Please note that this is not a sponsored post. I have purchased all of the equipment mentioned in this post with my own money. I have been using this equipment for a while now and I am very happy with it. I hope you find this post helpful. All the links are to Amazon and I am an Amazon Associate. If you purchase any of the equipment through the links in this post, I will get a small commission at no extra cost to you. This helps me keep this blog running. Thank you for your support.</p>

<p>Do you have a device that you can’t live without when you are traveling? Let me know in the comments below. I would love to hear about it.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Technology" /><category term="Productivity" /><category term="presenting" /><category term="Presentations" /><summary type="html"><![CDATA[This month in a chat with the speakers of ESPC one of the United States based speakers asked about some travel gear recommendations. I shared my travel gear with them and they found it helpful. I thought it would be a good idea to share it with you all. I have been using this gear for a while now and I am very happy with it. I hope you find it helpful too.]]></summary></entry><entry><title type="html">Power Prompt with Windows Terminal and Oh My Posh</title><link href="https://www.josephguadagno.net/2024/07/26/power-prompt-with-windows-terminal-and-oh-my-posh" rel="alternate" type="text/html" title="Power Prompt with Windows Terminal and Oh My Posh" /><published>2024-07-26T21:55:00-07:00</published><updated>2025-02-05T19:20:00-07:00</updated><id>https://www.josephguadagno.net/2024/07/26/power-prompt-with-windows-terminal-and-oh-my-posh</id><content type="html" xml:base="https://www.josephguadagno.net/2024/07/26/power-prompt-with-windows-terminal-and-oh-my-posh"><![CDATA[<p>In this post, I’ll show you how you can “power-up” your PowerShell shell prompt terminal window with Windows Terminal, PowerShell Core, and Oh-My-Posh to improve your day to day command line activity.</p>

<h2 id="getting-started">Getting Started</h2>

<h3 id="install-windows-terminal">Install Windows Terminal</h3>

<p>If you don’t have Windows Terminal installed, you can download it from the <a href="https://apps.microsoft.com/detail/9n0dx20hk701?rtc=1&amp;hl=en-us&amp;gl=US" target="_blank">Microsoft Store</a> (recommended by Microsoft), or you can download it from the <a href="https://github.com/microsoft/terminal/releases" target="_blank">Windows Terminal GitHub repository</a>.</p>

<p>Another option is to install it using the <a href="https://github.com/microsoft/winget-cli" target="_blank">Windows Package Manager</a>.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">winget</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--id</span><span class="w"> </span><span class="nx">Microsoft.WindowsTerminal</span><span class="w"> </span><span class="nt">--exact</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="install-powershell">Install PowerShell</h3>

<p>You can install PowerShell using the Windows Package Manager.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">winget</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--id</span><span class="w"> </span><span class="nx">Microsoft.PowerShell</span><span class="w"> </span><span class="nt">--source</span><span class="w"> </span><span class="nx">winget</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>If you want, you can install the latest preview version of PowerShell with the following command.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">winget</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--id</span><span class="w"> </span><span class="nx">Microsoft.PowerShell.Preview</span><span class="w"> </span><span class="nt">--source</span><span class="w"> </span><span class="nx">winget</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="install-oh-my-posh">Install Oh-My-Posh</h3>

<p><a href="https://ohmyposh.dev/" target="_blank">Oh-My-Posh</a> is a prompt theming engine for PowerShell. You can install it using the Windows Package Manager with the following command.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">winget</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nt">--id</span><span class="w"> </span><span class="nx">JanDeDobbeleer.OhMyPosh</span><span class="w"> </span><span class="nt">--exact</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="installing-with-unigetui">Installing with UniGetUI</h3>

<p>If you are using <a href="https://www.marticliment.com/unigetui/" target="_blank">UniGetUI</a>, which I highly recommend, you can install all three packages as a bundle.</p>

<ul>
  <li>Save this <em>json</em> locally on your machine.</li>
  <li>In UniGetUI, click on <em>Package Bundles</em></li>
  <li>Click on <em>Open existing bundle</em>, select the recently saved <em>json</em> file.</li>
  <li>Click on <em>Install selection</em></li>
</ul>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"export_version"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w">
  </span><span class="nl">"packages"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"JanDeDobbeleer.OhMyPosh"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Oh My Posh"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"23.0.2"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"winget"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"ManagerName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Winget"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"InstallationOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"SkipHashCheck"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"InteractiveInstallation"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"RunAsAdministrator"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"Architecture"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"InstallationScope"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"CustomParameters"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"PreRelease"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"CustomInstallLocation"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"Updates"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"UpdatesIgnored"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"IgnoredVersion"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Microsoft.PowerShell"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PowerShell"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"7.4.4.0"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"winget"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"ManagerName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Winget"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"InstallationOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"SkipHashCheck"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"InteractiveInstallation"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"RunAsAdministrator"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"Architecture"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"InstallationScope"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"CustomParameters"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"PreRelease"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"CustomInstallLocation"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"Updates"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"UpdatesIgnored"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"IgnoredVersion"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Microsoft.WindowsTerminal"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Windows Terminal"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.20.11781.0"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"winget"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"ManagerName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Winget"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"InstallationOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"SkipHashCheck"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"InteractiveInstallation"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"RunAsAdministrator"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"Architecture"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"InstallationScope"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"CustomParameters"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"PreRelease"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"CustomInstallLocation"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
        </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"Updates"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"UpdatesIgnored"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"IgnoredVersion"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.19.10573.0"</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"incompatible_packages_info"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Incompatible packages cannot be installed from WingetUI, but they have been listed here for logging purposes."</span><span class="p">,</span><span class="w">
  </span><span class="nl">"incompatible_packages"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="getting-started-with-oh-my-posh">Getting Started with Oh-My-Posh</h2>

<p>Oh-My-Posh allows you to customize your prompts with different <a href="https://ohmyposh.dev/docs/themes" target="_blank">themes</a> and <a href="https://ohmyposh.dev/docs/configuration/segment" target="_blank">segments</a>. A sample prompt looks like this.</p>

<p><img src="/assets/images/posts/2024/power-prompt-with-windows-terminal-and-oh-my-posh/oh-my-posh-hero.png" alt="Oh-My-Posh - Sample Prompt" class="align-center" /></p>

<p>If you just installed, Windows Terminal, Powershell, or Oh-My-Posh, I recommend you restart your terminal so that oh-my-posh is included in the path.</p>

<h3 id="font-installation">Font Installation</h3>

<p>After restarting your terminal, you can install a <a href="https://ohmyposh.dev/docs/installation/fonts" target="_blank">font</a>. You’ll typically want to install a Nerd Font, which includes the icons used in the prompt. Run the following command to install the <code class="language-plaintext highlighter-rouge">Meslo LGM NF</code> font.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">oh-my-posh</span><span class="w"> </span><span class="nx">font</span><span class="w"> </span><span class="nx">install</span><span class="w"> </span><span class="nx">meslo</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="register-the-font-with-windows-terminal">Register the font with Windows Terminal</h4>

<p>You will need to tell Windows Terminal to use this font. To do this, open up the <em>Settings</em> in Windows Terminal (<code class="language-plaintext highlighter-rouge">CTRL+,</code>)</p>

<ul>
  <li>Click on the <em>Defaults</em> profile.</li>
  <li>Under the <em>Additional settings</em> section, click <em>Appearance</em></li>
  <li>Under the <em>Fonts</em> section, select the <code class="language-plaintext highlighter-rouge">MesloLGM Nerd Font</code> font for the <em>Font face</em> property, or whatever font you installed.</li>
</ul>

<p>You can also do this by editing the Windows Terminal <code class="language-plaintext highlighter-rouge">settings.json</code> file. Add the following to the <code class="language-plaintext highlighter-rouge">defaults</code> profile.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
    </span><span class="nl">"profiles"</span><span class="p">:</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="nl">"defaults"</span><span class="p">:</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"font"</span><span class="p">:</span><span class="w">
            </span><span class="p">{</span><span class="w">
                </span><span class="nl">"face"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MesloLGM Nerd Font"</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="initializing-oh-my-posh">Initializing Oh-My-Posh</h3>

<p>In Windows Terminal, with the PowerShell prompt loaded, you will need to edit your <code class="language-plaintext highlighter-rouge">$PROFILE</code> file. You can do this by running the following command.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">notepad</span><span class="w"> </span><span class="bp">$PROFILE</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Add the following lines to the <code class="language-plaintext highlighter-rouge">$PROFILE</code> file.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">oh-my-posh</span><span class="w"> </span><span class="nx">init</span><span class="w"> </span><span class="nx">pwsh</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Invoke-Expression</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>This will initialize Oh-My-Posh with the default theme.</p>

<p>Save the file and either restart the terminal or run the following command to reload the profile.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="o">.</span><span class="w"> </span><span class="bp">$PROFILE</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="customizing-the-prompt">Customizing the Prompt</h2>

<p>There are many ways to customize the prompt. You can change the theme, add segments, or create your own theme. You can find more information on the customizing Oh-My-Posh in their <a href="https://ohmyposh.dev/docs/" target="_blank">documentation</a>.  I’ll show you the customizations I made to my prompt.  Once done, your prompt will look like this.</p>

<p><img src="/assets/images/posts/2024/power-prompt-with-windows-terminal-and-oh-my-posh/full-terminal.png" alt="Oh-My-Posh - Joe's Terminal" class="align-center" /></p>

<p>First step is to choose a theme. Themes in Oh-My-Posh are more then just colors, they include segments, which are the different parts of the prompt. I chose the <em><a href="https://ohmyposh.dev/docs/themes#powerlevel10k_modern" target="_blank">PowerLevel10k Modern</a></em> theme.  You can set the theme by editing your <code class="language-plaintext highlighter-rouge">oh-my-posh</code> initialization command in your <code class="language-plaintext highlighter-rouge">$PROFILE</code> file.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">oh-my-posh</span><span class="w"> </span><span class="nt">--init</span><span class="w"> </span><span class="nt">--shell</span><span class="w"> </span><span class="nx">pwsh</span><span class="w"> </span><span class="nt">--config</span><span class="w"> </span><span class="s2">"</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">POSH_THEMES_PATH</span><span class="s2">\powerlevel10k_modern.omp.json"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Invoke-Expression</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>This will load the theme from the <code class="language-plaintext highlighter-rouge">powerlevel10k_modern.omp.json</code> file.  You can find the theme files in the <code class="language-plaintext highlighter-rouge">%POSH_THEMES_PATH%</code> folder. This is likely in the following folder: <code class="language-plaintext highlighter-rouge">C:\Users\&lt;username&gt;\AppData\Local\Programs\oh-my-posh\themes</code> directory. There are many <a href="https://ohmyposh.dev/docs/themes" target="_blank">themes</a> to choose from, and you can create your own.</p>

<p><img src="/assets/images/posts/2024/power-prompt-with-windows-terminal-and-oh-my-posh/joe-powerlevel10k-modern-prompt.png" alt="Oh-My-Posh - Joe's Prompt (PowerLevel10K Modern Theme)" class="align-center" /></p>

<p class="notice"><strong><em>Note</em></strong>: You will not see the <code class="language-plaintext highlighter-rouge">main</code> portion of the prompt unless you install the <code class="language-plaintext highlighter-rouge">posh-git</code> module.  I’ll show you how to do that later.</p>

<p>If you want to customize the theme, you can create your own theme file.  You can find more information in the <a href="https://ohmyposh.dev/docs/configuration/general" target="_blank">Oh-My-Posh configuration documentation</a>.</p>

<h3 id="add-additional-powershell-modules">Add Additional PowerShell Modules</h3>

<p>You can add additional PowerShell modules to your prompt to further customize it.  I added the <code class="language-plaintext highlighter-rouge">posh-git</code>, <code class="language-plaintext highlighter-rouge">PSReadLine</code>, and <code class="language-plaintext highlighter-rouge">Terminal-Icons</code> modules to my prompt.</p>

<h4 id="posh-git-module">Posh-Git Module</h4>

<p><a href="https://github.com/dahlbyk/posh-git" target="_blank">Posh-git</a> provides prompt with Git status summary information and tab completion for Git commands, parameters, remotes and branch names.</p>

<p>You can install it as a PowerShell module. In your terminal, run the following command.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Install-Module</span><span class="w"> </span><span class="nx">posh-git</span><span class="w"> </span><span class="nt">-Scope</span><span class="w"> </span><span class="nx">CurrentUser</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>After installed, open your <code class="language-plaintext highlighter-rouge">$PROFILE</code> file and add the following line after your <code class="language-plaintext highlighter-rouge">oh-my-posh</code> initialization.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Import-Module</span><span class="w"> </span><span class="nx">posh-git</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="psreadline-module">PSReadLine Module</h4>

<p><a href="https://github.com/PowerShell/PSReadLine" target="_blank">PSReadLine</a> enhances the PowerShell command line editing environment. It provides syntax coloring, multi-line editing, and more.</p>

<p>You can install it as a PowerShell module. In your terminal, run the following command.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Install-Module</span><span class="w"> </span><span class="nx">PSReadLine</span><span class="w"> </span><span class="nt">-Scope</span><span class="w"> </span><span class="nx">CurrentUser</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>After installed, open your <code class="language-plaintext highlighter-rouge">$PROFILE</code> file and add the following line after your <code class="language-plaintext highlighter-rouge">oh-my-posh</code> initialization.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Import-Module</span><span class="w"> </span><span class="nx">PSReadLine</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>I also added the following lines to my <code class="language-plaintext highlighter-rouge">$PROFILE</code> file to customize the PSReadLine module after all of the imports.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">Set-PSReadLineOption</span><span class="w"> </span><span class="nt">-PredictionSource</span><span class="w"> </span><span class="nx">History</span><span class="w">
</span><span class="n">Set-PSReadLineOption</span><span class="w"> </span><span class="nt">-PredictionViewStyle</span><span class="w"> </span><span class="nx">ListView</span><span class="w">
</span><span class="n">Set-PSReadLineOption</span><span class="w"> </span><span class="nt">-EditMode</span><span class="w"> </span><span class="nx">Windows</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Option</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>PredictionSource</td>
      <td>The source of the prediction data.  In this case, it is the history.</td>
    </tr>
    <tr>
      <td>PredictionViewStyle</td>
      <td>The style of the prediction view.  In this case, it is a list view.</td>
    </tr>
    <tr>
      <td>EditMode</td>
      <td>The edit mode.  In this case, it is Windows.</td>
    </tr>
  </tbody>
</table>

<p>With these options, you can use the up and down arrow keys to cycle through your command history. As shown in the image below.</p>

<p><img src="/assets/images/posts/2024/power-prompt-with-windows-terminal-and-oh-my-posh/psreadline-options.png" alt="PSReadLine - Command History" class="align-center" /></p>

<p>Check out the <a href="https://learn.microsoft.com/en-us/powershell/module/psreadline/set-psreadlineoption?view=powershell-7.4&amp;?wt.mc_id=DT-MVP-4024623#parameters" target="_blank">Set-PSReadLineOption</a> documentation for more options.</p>

<h4 id="terminal-icons-module">Terminal-Icons Module</h4>

<p><a href="https://github.com/devblackops/Terminal-Icons" target="_blank">Terminal Icons</a> that adds file and folder icons when displaying items in the terminal.</p>

<p>You can install it as a PowerShell module. In your terminal, run the following command.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Install-Module</span><span class="w"> </span><span class="nx">Terminal-Icons</span><span class="w"> </span><span class="nt">-Scope</span><span class="w"> </span><span class="nx">CurrentUser</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>After installed, open your <code class="language-plaintext highlighter-rouge">$PROFILE</code> file and add the following line after your <code class="language-plaintext highlighter-rouge">oh-my-posh</code> initialization.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Terminal-Icons</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>After installing and importing the modules, your prompt will look like this when executing a <code class="language-plaintext highlighter-rouge">dir</code> command.</p>

<p><img src="/assets/images/posts/2024/power-prompt-with-windows-terminal-and-oh-my-posh/prompt-with-terminal-icons.png" alt="Oh-My-Posh - Joe's Prompt dir with Terminal-Icons" class="align-center" /></p>

<h3 id="complete-profile-file">Complete $PROFILE File</h3>

<p>Here is what my <code class="language-plaintext highlighter-rouge">$PROFILE</code> file looks like, with respect to the <em>oh-my-posh</em> configuration, after adding all of the modules.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">oh-my-posh</span><span class="w"> </span><span class="nt">--init</span><span class="w"> </span><span class="nt">--shell</span><span class="w"> </span><span class="nx">pwsh</span><span class="w"> </span><span class="nt">--config</span><span class="w"> </span><span class="s2">"</span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">POSH_THEMES_PATH</span><span class="s2">\powerlevel10k_modern.omp.json"</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Invoke-Expression</span><span class="w">
</span><span class="nx">Import-Module</span><span class="w"> </span><span class="nx">posh-git</span><span class="w">
</span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">PSReadLine</span><span class="w">
</span><span class="n">Import-Module</span><span class="w"> </span><span class="nx">Terminal-Icons</span><span class="w">
</span><span class="n">Set-PSReadLineOption</span><span class="w"> </span><span class="nt">-PredictionSource</span><span class="w"> </span><span class="nx">History</span><span class="w">
</span><span class="n">Set-PSReadLineOption</span><span class="w"> </span><span class="nt">-PredictionViewStyle</span><span class="w"> </span><span class="nx">ListView</span><span class="w">
</span><span class="n">Set-PSReadLineOption</span><span class="w"> </span><span class="nt">-EditMode</span><span class="w"> </span><span class="nx">Windows</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="wrap-up">Wrap up</h2>

<p>Now you have a customized PowerShell prompt with Windows Terminal and Oh-My-Posh.  You can customize the prompt further by creating your own theme or adding additional segments.  You can also add additional PowerShell modules to enhance your command line experience.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://learn.microsoft.com/en-us/windows/terminal/install?wt.mc_id=DT-MVP-4024623" target="_blank">Windows Terminal</a></li>
  <li><a href="https://ohmyposh.dev/" target="_blank">Oh-My-Posh</a></li>
  <li>Oh-My-Posh <a href="https://ohmyposh.dev/docs/themes" target="_blank">Themes</a></li>
  <li><a href="https://github.com/dahlbyk/posh-git" target="_blank">Posh-git</a></li>
  <li><a href="https://github.com/PowerShell/PSReadLine" target="_blank">PSReadLine</a></li>
  <li><a href="https://github.com/devblackops/Terminal-Icons" target="_blank">Terminal Icons</a></li>
  <li><a href="https://learn.microsoft.com/en-us/powershell/module/psreadline/set-psreadlineoption?view=powershell-7.4&amp;?wt.mc_id=DT-MVP-4024623#parameters" target="_blank">Set-PSReadLineOption</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Terminal" /><category term="PowerShell" /><category term="git" /><category term="oh-my-posh" /><category term="Productivity" /><summary type="html"><![CDATA[In this post, I’ll show you how you can “power-up” your PowerShell shell prompt terminal window with Windows Terminal, PowerShell Core, and Oh-My-Posh to improve your day to day command line activity.]]></summary></entry><entry><title type="html">Extended Validation (EV) Code Signing Certificates with Azure Key Vault and digicert</title><link href="https://www.josephguadagno.net/2024/07/17/ev-code-signing-certificates-with-azure-key-vault-and-digicert" rel="alternate" type="text/html" title="Extended Validation (EV) Code Signing Certificates with Azure Key Vault and digicert" /><published>2024-07-17T21:01:00-07:00</published><updated>2024-07-17T21:01:00-07:00</updated><id>https://www.josephguadagno.net/2024/07/17/ev-code-signing-certificates-with-azure-key-vault-and-digicert</id><content type="html" xml:base="https://www.josephguadagno.net/2024/07/17/ev-code-signing-certificates-with-azure-key-vault-and-digicert"><![CDATA[<p>In a previous post, I showed you how to <a href="/2020/04/04/setup-code-signing-certificates-in-azure-key-vault">Setup Code Signing Certificates in Azure Key Vault</a>.  In this post, I will show you how to create an Extended Validation (EV) Code Signing Certificate with digicert and store it in Azure Key Vault.</p>

<h2 id="prerequisites">Prerequisites</h2>

<p>For this to work, you will need the following:</p>

<ul>
  <li>Azure Subscription</li>
  <li><a href="https://learn.microsoft.com/en-us/azure/key-vault/general/overview?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault</a> Premium SKU</li>
  <li><a href="https://www.digicert.com" target="_blank">digicert</a> Account</li>
  <li>digicert EV Code Signing Certificate</li>
  <li>An organization that has been validated for EV Code Signing Certificates with digicert</li>
</ul>

<h2 id="background">Background</h2>

<p>What is a code signing certificate you ask? I’m glad you asked! According to <a href="https://www.globalsign.com/en/code-signing-certificate" target="_blank">digicert</a> code signing certificates are used by software developers to digitally sign apps, drivers, and software programs as a way for end-users to verify that the code they receive has not been altered or compromised by a third party. They include your signature, your company’s name, and if desired, a timestamp.</p>

<p>Essentially, they are a way to validate that source code, packages, executables, etc., comes from who it says it’s from, similarly to how certificates on a web site authenticate the web site when you go to HTTPS enabled web site.</p>

<p>EV Code Signing Certificates combine all the benefits of regular code signing plus additional features including:</p>

<ul>
  <li>Company address and organization type displayed in the certificate</li>
  <li>Time stamping, signature does not expire once certificate expires</li>
  <li>Certificate stored on a hardware token for two-factor authentication</li>
</ul>

<p>You can purchase a code signing certificate from a few different sources like <a href="https://digicert.com/code-signing/" target="_blank">digicert</a>, who I have my code certificate from. Please note, they can be expensive for an individual.</p>

<h2 id="creating-a-azure-key-vault">Creating a Azure Key Vault</h2>

<p>If have an Azure Key Vault Premium SKU, you can skip this step and move to 
<a href="/2024/07/17/ev-code-signing-certificates-with-azure-key-vault-and-digicert#generate-the-certificate-signing-request-csr-in-azure-key-vault">Generate the Certificate Signing Request (CSR) in Azure Key Vault</a>.</p>

<p>If you do not have one, you will need to create one.  Here are the steps to create one.</p>

<ul>
  <li>Sign in to the <a href="https://portal.azure.com#home">Azure Portal</a></li>
  <li>Click <em>Create a Resource</em></li>
  <li>In the *Search service and marketplace’ text box, type <code class="language-plaintext highlighter-rouge">Key Vault</code> and hit ‘Enter’</li>
  <li>Click <em>Create</em> on the Key Vault item, then <em>Create</em> again.</li>
</ul>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/create-key-vault.png" alt="Setup Code Signing Certificates - Key Vault Create" class="align-center" /></p>

<h3 id="key-vault-creation">Key Vault Creation</h3>

<p>You’ll be presented with the ‘Create key vault’ wizard.  For the most part, you can leave all of the defaults.</p>

<p><img src="/assets/images/posts/code-sign-key-vault-creation.png" alt="Setup Code Signing Certificates - Key Vault Creation" class="align-center" /></p>

<p>If you want to follow along, here are the settings I used.</p>

<table>
  <thead>
    <tr>
      <th>Setting</th>
      <th>Value</th>
      <th>Comments</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Subscription</td>
      <td><em>my subscription</em></td>
      <td> </td>
    </tr>
    <tr>
      <td>Resource Group</td>
      <td><code class="language-plaintext highlighter-rouge">Presentations</code></td>
      <td>I put all of my presentation and blog resources in this one group</td>
    </tr>
    <tr>
      <td>Key Vault Name</td>
      <td><code class="language-plaintext highlighter-rouge">Code-Signing-Demo-Vault</code></td>
      <td><strong>Note</strong>: This must be unique</td>
    </tr>
    <tr>
      <td>Region</td>
      <td><code class="language-plaintext highlighter-rouge">(US) West US 2</code></td>
      <td>You should choose a region close to your resources</td>
    </tr>
    <tr>
      <td>Pricing tier</td>
      <td>Premium (includes support for HSM backed keys</td>
      <td>Premium is required to manage the key</td>
    </tr>
  </tbody>
</table>

<p class="notice--warning"><strong><em>Note</em></strong>: You will need to have the Premium SKU to store the EV Code Signing Certificate in Azure Key Vault and the HSM encryption key</p>

<p>The rest are defaults.</p>

<p>Click <em>Review + Create</em></p>

<p>If you did everything correctly, you will get a <em>Validation passed</em>.  Validation feels good, doesn’t it!</p>

<p>Click <em>Create</em></p>

<p>After a few moments, the vault will be created and you will have a page similar to this.  I blurred out the secret stuff.</p>

<p><img src="/assets/images/posts/code-sign-key-vault-overview.png" alt="Setup Code Signing Certificates - Key Vault Overview" class="align-center" /></p>

<p>Click on <em>Go to resource</em></p>

<p>Now that you have a Key Vault that is capable of storing the EV Code Signing Certificate, you can move on to the next step.</p>

<h2 id="generate-the-certificate-signing-request-csr-in-azure-key-vault">Generate the Certificate Signing Request (CSR) in Azure Key Vault</h2>

<p>The next step is to generate the Certificate Signing Request (CSR) in Azure Key Vault.  This is the request that you will send to digicert to create the EV Code Signing Certificate. Here are the steps to generate the CSR.</p>

<ul>
  <li>Click <em>Objects</em> in the left-hand menu</li>
  <li>Click <em>Certificates</em></li>
  <li>Click <em>+Generate/Import</em></li>
</ul>

<h3 id="certification-creation-permissions-required">Certification Creation Permissions Required</h3>

<p>If you see a message stating that “The operation is not allowed by RBAC…’ you will need to grant yourself the correct permissions.</p>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/operation-not-allowed-by-rbac.png" alt="The operation is not allow by RBAC" /></p>

<p>You need the role of <code class="language-plaintext highlighter-rouge">Key Vault Certificates Officer</code>. You can do this by following these steps:</p>

<ul>
  <li>Click on the <em>Access Control (IAM)</em> in the left-hand menu</li>
  <li>Click on the <em>+ Add</em>, then choose <em>Role Assignment</em></li>
  <li>Click on <em>Key Vault Certificates Officer</em></li>
  <li>Click <em>Next</em></li>
  <li>Click on <em>Select members</em> and choose your account, it should be first.</li>
  <li>Click <em>Select</em></li>
  <li>Click <em>Review + Assign</em></li>
  <li>Click <em>Review + Assign</em> again.</li>
</ul>

<p>Now you can go back to the <em>Objects</em> -&gt; <em>Certificates</em> -&gt; <em>+Generate/Import</em>.</p>

<h3 id="generate-the-csr">Generate the CSR</h3>

<p>Now that you are at the <em>Generate/Import</em> page, you can generate the CSR.</p>

<p>Enter/Select the following values:</p>

<table>
  <tbody>
    <tr>
      <td>Field</td>
      <td>Value</td>
      <td>Comments</td>
    </tr>
    <tr>
      <td>Method of Certificate Creation</td>
      <td>Generate</td>
      <td> </td>
    </tr>
    <tr>
      <td>Certificate Name</td>
      <td><code class="language-plaintext highlighter-rouge">CodeSigning-2024</code></td>
      <td>This is the name of the certificate in the Key Vault. <strong>NOTE</strong>: I append a year to the certificate name since they expire, typically in a year, but this is not required</td>
    </tr>
    <tr>
      <td>Type of Certificate Authority (CA)</td>
      <td>‘Certificate issued by a non-integrated CA’</td>
      <td>If you have an integrated CA, you can chose that, I don’t know if the rest of the process would be the same</td>
    </tr>
    <tr>
      <td>Subject</td>
      <td><code class="language-plaintext highlighter-rouge">CN=YourDomain.Com, O=YourCompanyName, L=YourCity, S=YourState, C=YourCountry</code></td>
      <td>This is the subject of the certificate.  You can change this to your information. <strong>Note</strong>: See notes after this table for more details around the subject</td>
    </tr>
    <tr>
      <td>DNS Names</td>
      <td>Left blank</td>
      <td>This is for a SAN certificate, which is not needed for a code signing certificate</td>
    </tr>
    <tr>
      <td>Validity Period (in months)</td>
      <td>12</td>
      <td>This is the number of months the certificate is valid for.  You can change this to a different number if you want depending on what your CA supports</td>
    </tr>
    <tr>
      <td>Content Type</td>
      <td>PKCS#12</td>
      <td>This is the format of the certificate.  You can use PKCS#12 or PEM.  I use PKCS#12 since it is a binary format and can be used in Windows easily</td>
    </tr>
    <tr>
      <td>Lifetime Action Type</td>
      <td><em>Choice is yours</em></td>
      <td>This is when Azure Key Vault will notify you that the certificate is about to expire.  You can choose a number of days before or a percentage of the overall time. The next option will allow you to chose a number of days or percentage</td>
    </tr>
  </tbody>
</table>

<h4 id="subject-comments">Subject Comments</h4>

<table>
  <thead>
    <tr>
      <th>Field</th>
      <th>Value</th>
      <th>Comments</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CN</td>
      <td>YourDomain.Com</td>
      <td>This is the common name of the certificate.  This should be the domain of the certificate.  If you are using it for code signing, you can use the domain of your company or your personal domain</td>
    </tr>
    <tr>
      <td>O</td>
      <td>YourCompanyName</td>
      <td>This is the organization name.  This should be the name of your company or organization</td>
    </tr>
    <tr>
      <td>L</td>
      <td>YourCity</td>
      <td>This is the city where your company is located</td>
    </tr>
    <tr>
      <td>S</td>
      <td>YourState</td>
      <td>This is the state where your company is located</td>
    </tr>
    <tr>
      <td>C</td>
      <td>YourCountry</td>
      <td>This is the country where your company is located.  This should be the two-letter country code</td>
    </tr>
  </tbody>
</table>

<p>If any of the values have spaces, you will need to wrap the value in double quotes.  For example, if you are in New York City, you would use <code class="language-plaintext highlighter-rouge">L="New York City"</code>.</p>

<p>Before you click <em>Create</em>, you will need to click on <em>Not configured</em> next to <em>Advanced Policy Configuration</em> and enter the following values:</p>

<h4 id="advanced-policy-configuration">Advanced Policy Configuration</h4>

<p>The Advanced Policy Configuration is where you can set the permissions for the certificate.  You can set the permissions for the certificate to be used for code signing, encryption, etc.  Here are the values I used for the certificate.</p>

<table>
  <tbody>
    <tr>
      <td>Field</td>
      <td>Value</td>
      <td>Comments</td>
    </tr>
    <tr>
      <td>Extended Key Usages (EKUs)</td>
      <td><code class="language-plaintext highlighter-rouge">1.3.6.1.5.5.7.3.1, 1.3.6.1.4.1.311.10.3.13</code></td>
      <td>This is the list of EKUs that the certificate will be used for. The first value is SSL/TLS Web Server Authentication and the second is Code Signing.  You can add more EKUs if you need to</td>
    </tr>
    <tr>
      <td>X.509 Key Usage Flags</td>
      <td><code class="language-plaintext highlighter-rouge">DigitalSignature, KeyEncipherment</code></td>
      <td>This is the list of key usage flags.  This is a comma-separated list of the key usage flags.  The values are DigitalSignature, NonRepudiation, KeyEncipherment, DataEncipherment, KeyAgreement, CertificateSigning, CRLSigning, EncipherOnly, and DecipherOnly.  You can add more flags if you need to</td>
    </tr>
    <tr>
      <td>Reuse Key on Renewal</td>
      <td>No</td>
      <td>This is whether the key should be reused when the certificate is renewed.  I chose No since I want a new key when the certificate is renewed</td>
    </tr>
    <tr>
      <td>Exportable Private Key</td>
      <td>No</td>
      <td>This is whether the private key can be exported.  I chose No since I don’t want the private key to be exported</td>
    </tr>
    <tr>
      <td>Key Type</td>
      <td>RSA-HSM</td>
      <td>This is the type of key to use.  Code Signing certificates need to be backed by an HSM</td>
    </tr>
    <tr>
      <td>Key Size</td>
      <td>4096</td>
      <td>This is the size of the key.  I chose 4096 since it is the most secure</td>
    </tr>
    <tr>
      <td>Enable Certificate Transparency</td>
      <td>No</td>
      <td>This is whether the certificate should be logged in a certificate transparency log.  I chose No since I don’t need it</td>
    </tr>
    <tr>
      <td>Certificate Type</td>
      <td><em>leave blank</em></td>
      <td>This is the type of certificate.  I left it blank since I don’t need to specify a type</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/advanced-policy-configuration.png" alt="Advanced Policy Configuration" class="align-center" /></p>

<ul>
  <li>Click <em>OK</em> to save the Advanced Policy Configuration.</li>
  <li>Click <em>Create</em> to generate the CSR.</li>
</ul>

<p>At this point, the CSR will be generated.  You will need to download the CSR and send it to digicert to create the EV Code Signing Certificate.  Click the following prompt in the portal</p>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/certificate-creation-pending.png" alt="Certificate Creation Pending" /></p>

<p>This will take you to a blade that will allow you to download the CSR which will be required by digicert to create the EV Code Signing Certificate.</p>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/certificate-in-progress.png" alt="Certificate Creation in Progress" /></p>

<ul>
  <li>Click on <em>Download CSR</em> to download the CSR.</li>
  <li>Save the file to a location on your computer.</li>
</ul>

<p>The default filename should be something like <code class="language-plaintext highlighter-rouge">&lt;certificate-name&gt;_&lt;guid&gt;.csr</code>.  You can rename the file to something more meaningful if you want but you will only use the file in the next step and then it can be deleted.</p>

<h2 id="create-an-ev-code-signing-certificate">Create an EV Code Signing Certificate</h2>

<p>Login to your <a href="https://www.digicert.com" target="_blank">digicert</a> account and follow the steps to create a new certificate.</p>

<p>From the Dashboard, click on the <em>Request a Certificate</em> button and choose <em>EV Code Signing Certificate</em>.</p>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/request-a-certificate.png" alt="digicert Request a Certificate" class="align-center" /></p>

<h3 id="certificate-information">Certificate Information</h3>

<table>
  <tbody>
    <tr>
      <td>Field</td>
      <td>Value</td>
      <td>Comments</td>
    </tr>
    <tr>
      <td>Validity period (optional)</td>
      <td><code class="language-plaintext highlighter-rouge">1 year</code></td>
      <td>This is the validity period of the certificate.  For simplicity, it should match what you chose in the certificate creation for Azure Key Vault</td>
    </tr>
    <tr>
      <td>Auto-renew</td>
      <td><em>your choice</em></td>
      <td>This is whether the certificate should be automatically renewed.  I left it unchecked since I rotate the keys and certificates</td>
    </tr>
  </tbody>
</table>

<p>Click the <em>Add Organization</em> button to add your organization information.  This will be the information that is displayed in the certificate. Only organizations that have been validated for EV Code Signing Certificates can be used. For more on this, see the <a href="https://www.digicert.com/faq/public-trust-and-certificates/what-is-an-extended-validation-ev-ssl-certificate" target="_blank">digicert EV Code Signing Certificate</a> page.</p>

<p>The additional emails are for the certificate notifications.  You can add more emails if you want to be notified when the certificate is about to expire, etc.</p>

<p>For the <em>Provisioning options</em>, select <code class="language-plaintext highlighter-rouge">Install on an HSM</code>.  Once you select that option, you will asked <em>Was the private key generated by a Common Criteria EAL4+ standard or FIPS 140-2 level 2 HSM?</em>. You can chose <code class="language-plaintext highlighter-rouge">Yes</code> because Azure Key Vault is FIPS 140-2 level 2 compliant. After you selected <em>Yes</em>, you can upload the CSR that you downloaded from Azure Key Vault or paste the contents of the CSR into the text box.</p>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/certificate-provisioning-options.png" alt="digicert Certificate Provisioning Options" class="align-center" /></p>

<p>Click <em>Submit Request</em></p>

<p>After you pay for the certificate, you will be brought to a page that indicates that you successfully requested the certificate.  This all assumes, that your organization was already validated for EV Code Signing Certificates.  If not, you will need to go through the validation process.</p>

<p>You’ll also notice on this page, that you will have to sign a <strong><em>HSM Private key agreement</em></strong>.</p>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/hsm-private-key-agreement-required.png" alt="digicert HSM Private Key Agreement" class="align-center" /></p>

<p>At this point, you, or someone in your organization, will receive and email from digicert with the title <em>[Action Required] Private key protection requirements for digicert Order</em>.</p>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/hsm-agreement-email.png" alt="digicert Private Key Protection Requirements" class="align-center" /></p>

<p>After you agree to the terms, digicert will create the certificate and send you an email with the certificate, this can take some time.  After you receive the email, go back to the digicert portal <a href="https://www.digicert.com/secure/orders" target="_blank">order page</a> and click on the new order.</p>

<p>Click on the <em>Download certificates as</em> button and chose <code class="language-plaintext highlighter-rouge">Best format for FIPS 140-2 Level 2 or Common Criteria EAL4+</code>.  This will download a zip file with the certificate in the correct format for Azure Key Vault.</p>

<p>Extract the downloaded zip file to a location on your computer.</p>

<h2 id="import-the-ev-code-signing-certificate-into-azure-key-vault">Import the EV Code Signing Certificate into Azure Key Vault</h2>

<p>Navigate back to the Azure Key Vault in the Azure Portal where you generated the request.</p>

<ul>
  <li>Click on the <em>Objects</em> -&gt; <em>Certificates</em> -&gt; <em>certificate-name</em>.  This will bring you to the certificate blade.</li>
  <li>Click on the <em>Certificate Operation</em> button on top.</li>
  <li>Click on <em>Merged Signed Request</em>.</li>
  <li>Select the certificate for your organization from the extracted zip file. It should be <code class="language-plaintext highlighter-rouge">&lt;your_organization&gt;.crt</code>. In my case, the file name is <code class="language-plaintext highlighter-rouge">josephguadagno_net_llc.crt</code>.</li>
</ul>

<p>If successful, you will see a Completed status on the certificate operation. If you close this blade, you will notice that the certificate is now enabled with an activation date and expiration date. If you navigate back to the <em>Certificates</em> blade, you will see the certificate listed in the completed state.</p>

<p><img src="/assets/images/posts/2024/ev-code-signing-certificates-with-azure-key-vault/certificate-complete.png" alt="Certificate Operation Completed" class="align-center" /></p>

<h2 id="wrap-up">Wrap Up</h2>

<p>In this post, you learned how to create an Extended Validation (EV) Code Signing Certificate with digicert and store it in Azure Key Vault.</p>

<p>If you want to learn how to use the certificate to sign your a NuGet package from an Azure DevOps pipeline, check out the post <a href="/2020/04/12/build-sign-and-deploy-nuget-packages-with-azure-pipelines">Build, Sign, and Deploy NuGet Packages with Azure Pipeline</a>.</p>

<h2 id="reference">Reference</h2>

<ul>
  <li><a href="/2020/04/04/setup-code-signing-certificates-in-azure-key-vault" target="_blank">Setup Code Signing Certificates in Azure Key Vault</a></li>
  <li><a href="https://learn.microsoft.com/en-us/azure/key-vault/general/overview?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault</a></li>
  <li><a href="https://www.digicert.com" target="_blank">digicert</a></li>
  <li><a href="https://www.digicert.com/faq/public-trust-and-certificates/what-is-an-extended-validation-ev-ssl-certificate" target="_blank">What is an Extended Validation (EV) SSL Certificate?</a></li>
  <li><a href="/2020/04/12/build-sign-and-deploy-nuget-packages-with-azure-pipelines">Build, Sign, and Deploy NuGet Packages with Azure Pipeline</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Key Vault" /><category term="Microsoft Azure" /><category term="digicert" /><category term="Certificate" /><category term="Code Signing" /><summary type="html"><![CDATA[In a previous post, I showed you how to Setup Code Signing Certificates in Azure Key Vault. In this post, I will show you how to create an Extended Validation (EV) Code Signing Certificate with digicert and store it in Azure Key Vault.]]></summary></entry><entry><title type="html">Docker Volume Location on Windows</title><link href="https://www.josephguadagno.net/2024/07/13/docker-volume-location-on-windows" rel="alternate" type="text/html" title="Docker Volume Location on Windows" /><published>2024-07-13T08:40:00-07:00</published><updated>2025-01-11T20:10:00-07:00</updated><id>https://www.josephguadagno.net/2024/07/13/docker-volume-location-on-windows</id><content type="html" xml:base="https://www.josephguadagno.net/2024/07/13/docker-volume-location-on-windows"><![CDATA[<p>Like a lot of engineers nowadays, I use Docker for my development environment.  I have a Windows 11 machine with <a href="https://learn.microsoft.com/en-us/windows/wsl/about?wt.mc_id=DT-MVP-4024623" target="_blank">Windows Subsystem for Linux (WSL)</a> and I use Docker Desktop for Windows.  I also use Developer Containers with a volume for the source code to work with my blog, which means the files are not stored on the local file system but in a Docker volume.  Occasionally, I need to access the files in the Docker volume for image creation.  This post will show you how to access the files in the Docker volume on Windows.</p>

<p>First off, this post assumes you have Windows, either Windows 10 or Windows 11, with WSL and Docker Desktop installed.  If you don’t have Docker Desktop installed, you can download it from <a href="https://hub.docker.com/" target="_blank">Docker Hub</a>.</p>

<h2 id="docker-volume-location">Docker Volume Location</h2>

<p class="notice--info"><strong>Update</strong>: 2025-01-11: Docker changed the locations of the volumes with release <em>v26.1.4</em>.</p>

<p>When you use Docker Desktop on Windows, the Docker volumes are stored in the WSL file system.  The WSL file system is located at <code class="language-plaintext highlighter-rouge">\\wsl$\</code> on the Windows file system.</p>

<p>If you want to see what version of docker you are running, you can open a command prompt or Powershell and run the following command.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>docker <span class="nt">--version</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="current-docker-version-v2614-and-higher">Current Docker Version (v26.1.4 and Higher)</h3>

<p>The Docker volumes are stored in the WSL file system at <code class="language-plaintext highlighter-rouge">\\wsl$\docker-desktop\mnt\docker-desktop-disk\data\docker\volumes</code>.  You should see a folder for each volume you have created in Docker Desktop. <strong><em>Note</em></strong>: The <code class="language-plaintext highlighter-rouge">wsl.localhost</code> is no longer used in the path.</p>

<h3 id="previous-docker-version-v2613-and-lower">Previous Docker Version (v26.1.3 and Lower)</h3>

<p>The Docker volumes are stored in the WSL file system at <code class="language-plaintext highlighter-rouge">\\wsl.localhost\docker-desktop-data\data\docker\volumes</code>.  You should see a folder for each volume you have created in Docker Desktop.</p>

<p>Here is a screenshot of my Docker Desktop volumes.</p>

<p><img src="/assets/images/posts/2024/docker-volume-location-on-windows/docker-desktop-volumes.png" alt="Docker Volume Location" /></p>

<p>Here is a screenshot of the WSL file system with the Docker volumes.</p>

<p><img src="/assets/images/posts/2024/docker-volume-location-on-windows/windows-volumes.png" alt="Windows Explorer Volume" /></p>

<p>As you can see, there is a one to one mapping between the Docker Desktop volumes and the WSL file system.  If you access these volumes regularly outside of the containers, you might want to rename the folders to something more meaningful.</p>

<h3 id="accessing-the-volumes">Accessing the Volumes</h3>

<p>If you do access the volumes a lot, you can create a symbolic link to the volumes folder.  To do this, open a command prompt or Powershell as an administrator and run the following command.</p>

<p class="notice"><strong><em>Note</em></strong>: You can change the <code class="language-plaintext highlighter-rouge">C:\Volumes</code> to any folder you want to use.</p>

<h4 id="command-prompt">Command Prompt</h4>

<p>Version 26.1.4 and Higher</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>mklink /D C:<span class="se">\V</span>olumes <span class="se">\\</span>wsl<span class="nv">$\</span>docker-desktop<span class="se">\m</span>nt<span class="se">\d</span>ocker-desktop-disk<span class="se">\d</span>ata<span class="se">\d</span>ocker<span class="se">\v</span>olumes
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Version 26.1.3 and Lower</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>mklink /D C:<span class="se">\V</span>olumes <span class="se">\\</span>wsl.localhost<span class="se">\d</span>ocker-desktop-data<span class="se">\d</span>ata<span class="se">\d</span>ocker<span class="se">\v</span>olumes
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="powershell">PowerShell</h4>

<p>Version 26.1.4 and Higher</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>New-Item <span class="nt">-ItemType</span> SymbolicLink <span class="nt">-Path</span> <span class="s2">"c:</span><span class="se">\V</span><span class="s2">olumes"</span> <span class="nt">-Target</span> <span class="s2">"</span><span class="se">\\</span><span class="s2">wsl</span><span class="nv">$\</span><span class="s2">docker-desktop</span><span class="se">\m</span><span class="s2">nt</span><span class="se">\d</span><span class="s2">ocker-desktop-disk</span><span class="se">\d</span><span class="s2">ata</span><span class="se">\d</span><span class="s2">ocker</span><span class="se">\v</span><span class="s2">olumes"</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Version 26.1.3 and Lower</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>New-Item <span class="nt">-ItemType</span> SymbolicLink <span class="nt">-Path</span> <span class="s2">"c:</span><span class="se">\V</span><span class="s2">olumes"</span> <span class="nt">-Target</span> <span class="s2">"</span><span class="se">\\</span><span class="s2">wsl.localhost</span><span class="se">\d</span><span class="s2">ocker-desktop-data</span><span class="se">\d</span><span class="s2">ata</span><span class="se">\d</span><span class="s2">ocker</span><span class="se">\v</span><span class="s2">olumes"</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="file-explorer">File Explorer</h4>

<p>If you open up File Explorer, you should see a folder called <code class="language-plaintext highlighter-rouge">Volumes</code> in the root of the <code class="language-plaintext highlighter-rouge">C:\</code> drive.  This folder is a symbolic link to the Docker volumes.</p>

<p><img src="/assets/images/posts/2024/docker-volume-location-on-windows/docker-desktop-folder-highlighted.png" alt="Windows Explorer - Linux Docker Desktop Volumes" /></p>

<h2 id="wrap-up">Wrap Up</h2>

<p>In this post, you learned where the Docker volumes are stored when using Docker Desktop on Windows.  You also learned how to access the Docker volumes from the Windows file system and create shortcuts to them.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://stackoverflow.com/questions/61083772/where-are-docker-volumes-located-when-running-wsl-using-docker-desktop" target="_blank">Stack Overflow</a>.</li>
  <li><a href="https://stackoverflow.com/questions/43181654/locating-data-volumes-in-docker-desktop-windows" target="_blank">Stack Overflow</a>.</li>
  <li><a href="https://learn.microsoft.com/en-us/windows/wsl/about?wt.mc_id=DT-MVP-4024623" target="_blank">Windows Subsystem for Linux (WSL)</a>.</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Containers" /><category term="Docker" /><category term="Windows11" /><summary type="html"><![CDATA[Like a lot of engineers nowadays, I use Docker for my development environment. I have a Windows 11 machine with Windows Subsystem for Linux (WSL) and I use Docker Desktop for Windows. I also use Developer Containers with a volume for the source code to work with my blog, which means the files are not stored on the local file system but in a Docker volume. Occasionally, I need to access the files in the Docker volume for image creation. This post will show you how to access the files in the Docker volume on Windows.]]></summary></entry><entry><title type="html">My JetBrains Rider Plugins — July 2024 Edition</title><link href="https://www.josephguadagno.net/2024/07/12/my-jetbrains-rider-plugins-july-2024-edition" rel="alternate" type="text/html" title="My JetBrains Rider Plugins — July 2024 Edition" /><published>2024-07-12T21:27:00-07:00</published><updated>2024-07-31T15:43:00-07:00</updated><id>https://www.josephguadagno.net/2024/07/12/my-jetbrains-rider-plugins-july-2024-edition</id><content type="html" xml:base="https://www.josephguadagno.net/2024/07/12/my-jetbrains-rider-plugins-july-2024-edition"><![CDATA[<p class="notice--warning">I’ve updated my plugins list for JetBrains Rider.  You can find the updated list in the post <a href="/2025/08/23/my-jetbrains-rider-plugins-august-2025-edition" target="_blank">My JetBrains Rider Plugins — August 2025 Edition</a>.</p>

<p>I’ve been using <a href="https://www.jetbrains.com/" target="_blank">JetBrains</a> <a href="https://www.jetbrains.com/rider/" target="_blank">Rider</a> for quite some time now.</p>

<p>I started using JetBrains Rider because I used both a Windows PC and a Mac PC and I wanted a consistent experience between the both.
JetBrains Rider provided that and now I am hooked.</p>

<p>I thought I’d take some time to blog about the plugins I use to help with my day-to-day coding (mostly in Microsoft .NET and Azure), presenting (public speaking), and blogging (using <a href="https://jekyllrb.com/" target="_blank">Jekyll</a>).</p>

<p>All of these plugins can be downloaded from either the JetBrains plugin <a href="https://plugins.jetbrains.com/" target="_blank">Marketplace</a> or directly in the IDE.</p>

<p class="notice">This is an updated post from a previous post I wrote in <a href="/2022/06/23/my-jetbrains-rider-plugins-june-2022-edition" target="_blank">June 2022</a>.</p>

<p>So here is the list.</p>

<h2 id="general-development">General Development</h2>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/9525--env-files-support" target="_blank">.env files support</a></td>
      <td>As the name implies, it provides environment variable completion for <code class="language-plaintext highlighter-rouge">Dockerfile</code> and <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> files</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/17718-github-copilot" target="_blank">GitHub Copilot</a></td>
      <td>GitHub Copilot uses OpenAI Codex to suggest code and entire functions in real-time right from your editor. <strong><em>Note</em></strong>: This plugin is free but requires a paid subscription service</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/22282-jetbrains-ai-assistant" target="_blank">JetBrains AI Assistant</a></td>
      <td>JetBrains AI Assistant provides AI-powered features for software development based on the JetBrains AI Service. The service transparently connects you, as an IDE user, to different large language models (LLMs). <strong><em>Note</em></strong>: This plugin is free but requires a paid subscription service</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/9792-key-promoter-x" target="_blank">Key Promoter X</a></td>
      <td>Let’s you know if there is a keystroke shortcut for any mouse based IDE commands</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/12832-structured-logging" target="_blank">Structured Logging</a></td>
      <td>Contains some useful analyzers for structured logging. Supports Serilog, NLog, and Microsoft.Extensions.Logging</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/24653-uno-platform" target="_blank">Uno Platform</a></td>
      <td>The Uno Platform plugin for JetBrains Rider enables Hot Reload support for Uno Platform apps, streamlining the development of cross-platform .NET Mobile, Web, and Desktop Apps.</td>
    </tr>
  </tbody>
</table>

<h3 id="microsoft-specific-development">Microsoft Specific Development</h3>

<p>The only plugin in this category was a .NET user secret plugin. This is no longer needed as the IDE now supports this feature.</p>

<h3 id="azure-specific-development">Azure Specific Development</h3>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/13984-application-insights-debug-log-viewer" target="_blank">Application Insights Debug Log Viewer</a></td>
      <td>Provides the ability to view Azure Monitor (Application Insights) telemetry in the IDE</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/11220-azure-toolkit-for-rider" target="_blank">Azure Toolkit for Rider</a></td>
      <td>Rider plugin for integration with Azure cloud services. Allow to create, configure, and deploy .Net Core and .Net Web Apps to Azure from Rider on all supported platforms</td>
    </tr>
  </tbody>
</table>

<h2 id="presenting">Presenting</h2>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/18045-window-resizer" target="_blank">Window Resizer</a></td>
      <td>This plugin let you quickly resize windows to any of the following predefined orientations/sizes</td>
    </tr>
  </tbody>
</table>

<h2 id="writing">Writing</h2>

<p>I know longer have anything in this category since I use GitHub Copilot and JetBrains AI Assistant.</p>

<h2 id="no-longer-in-the-list">No longer in the list</h2>

<table>
  <tbody>
    <tr>
      <td>Plugin</td>
      <td>What it does</td>
      <td>Why did it drop</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/7345-presentation-assistant" target="_blank">Presentation Assistant</a></td>
      <td>This plugin shows name and Win/Mac shortcuts of any action you invoke</td>
      <td>Built into the IDE now</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/10249-powershell" target="_blank">PowerShell</a></td>
      <td>Provides PowerShell intellisense and script execution support for IntelliJ IDEs</td>
      <td>Turns out I don’t edit PowerShell scripts that often</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/10080-rainbow-brackets" target="_blank">Rainbow Brackets</a></td>
      <td>Provides colored brackets, parentheses, and lines in the IDE</td>
      <td>Built into the IDE now</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/2162-string-manipulation" target="_blank">String Manipulation</a></td>
      <td>Case switching, sorting, filtering, incrementing, aligning to columns, grepping, escaping, encoding… Very helpful when working with tabular data</td>
      <td>No longer a need for this plugin</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/7981-azure-devops" target="_blank">Azure DevOps</a></td>
      <td>Azure DevOps is a plugin to enable working with Git and TFVC repositories on Azure DevOps Services or Team Foundation Server (TFS) 2015+</td>
      <td>Built into the IDE now</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/12175-grazie" target="_blank">Grazie</a></td>
      <td>Intelligent spelling and grammar checks for any text you write in the IDE</td>
      <td>Between Github CoPilot and JetBrains AI Assistant, it covers what I need</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/16136-grazie-professional" target="_blank">Grazie Professional</a></td>
      <td>Enhances the base Grazie plugin with advanced writing assistance for English text in your IDE.</td>
      <td>Between Github CoPilot and JetBrains AI Assistant, it covers what I need</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/10183--net-core-user-secrets" target="_blank">.NET Core User Secrets</a></td>
      <td>Provides the ability to create and edit user secrets in .NET projects</td>
      <td>Built into the IDE now</td>
    </tr>
  </tbody>
</table>

<h2 id="wrap-up">Wrap Up</h2>

<p>You’ll also notice that a lot of the plugins I used to use are now built into the IDE.</p>

<p>Do you have a favorite not listed? Let me know.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term="JetBrains" /><category term="Rider" /><category term="Plugin" /><category term="presenting" /><summary type="html"><![CDATA[I’ve updated my plugins list for JetBrains Rider. You can find the updated list in the post My JetBrains Rider Plugins — August 2025 Edition.]]></summary></entry><entry><title type="html">Azure Key Vault - Update a Secret and its Properties Programmatically</title><link href="https://www.josephguadagno.net/2024/06/29/azure-key-vault-update-a-secret-and-its-properties-programmatically" rel="alternate" type="text/html" title="Azure Key Vault - Update a Secret and its Properties Programmatically" /><published>2024-06-29T13:18:00-07:00</published><updated>2024-06-29T13:18:00-07:00</updated><id>https://www.josephguadagno.net/2024/06/29/azure-key-vault-update-a-secret-and-its-properties-programmatically</id><content type="html" xml:base="https://www.josephguadagno.net/2024/06/29/azure-key-vault-update-a-secret-and-its-properties-programmatically"><![CDATA[<p>When you use the Azure Key Vault SDK, you can create, read, update, and delete secrets.  In this post, you will learn how to update a secret and its properties using the Azure Key Vault SDK.</p>

<p>I was working on the <a href="https://github.com/jguadagno/jjgnet-broadcast" target="_blank">JosephGuadagno.NET Broadcasting</a> application this weekend and wanted to create a feature that would automatically refresh the tokens that are used to authenticate with the various social media platforms. For reasons, these tokens were stored locally in the application settings.  I know, bad choice, but when I started this project I really didn’t know Azure Key Vault, so I went with what I knew.  I moved these tokens to Azure Key Vault, so now I needed to update the tokens in the Key Vault. However, when use Azure Key Vault SDK to update secrets, it creates an new version of the secret and doesn’t disable the previous secret.  Since I am refreshing the token, the old token is no longer valid, so I needed to disable the old secret.  At the same time, Azure Key Vaults secrets have an expiration date, so I wanted to update the expiration date of the secret as well.  Let’s take a look at how to do this.</p>

<p class="notice">This post assumes you have an Azure Key Vault instance and you know how to setup a secret.  If you don’t know how to set up a secret, you can follow the steps in the post <a href="/2020/07/10/securing-azure-function-settings-with-azure-key-vault" target="_blank">Securing Azure Function Settings with Azure Key Vault</a>.</p>

<h2 id="the-process">The Process</h2>

<p>There are a couple of steps to update a secret and its properties to achieve what I wanted to.  The steps are:</p>

<ol>
  <li>Establish a connection to the Azure Key Vault and obtain a <code class="language-plaintext highlighter-rouge">SecretClient</code> <a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretclient?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">class</a>.</li>
  <li>Get the current secret from the vault using the <code class="language-plaintext highlighter-rouge">GetSecretAsync</code> <a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretclient.getsecretasync?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">method</a> of the <code class="language-plaintext highlighter-rouge">SecretClient</code> class.</li>
  <li>Call the <code class="language-plaintext highlighter-rouge">UpdateSecretPropertiesAsync</code> <a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretclient.updatesecretproperties?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">method</a> of the <code class="language-plaintext highlighter-rouge">SecretClient</code> class to disable the secret.</li>
  <li>Update / create a new version of the secret using the <code class="language-plaintext highlighter-rouge">SetSecretAsync</code> <a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretclient.setsecretasync?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">method</a> of the <code class="language-plaintext highlighter-rouge">SecretClient</code> class</li>
  <li>Call the <code class="language-plaintext highlighter-rouge">UpdateSecretPropertiesAsync</code> <a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretclient.updatesecretproperties?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">method</a> of the <code class="language-plaintext highlighter-rouge">SecretClient</code> class using the <code class="language-plaintext highlighter-rouge">KeyVaultSecret</code> returned from the step 4 to update the expiration date of the secret.</li>
</ol>

<h3 id="the-code">The Code</h3>

<p>Here is the code to update a secret and its properties.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">UpdateSecretValueAndProperties</span><span class="p">(</span><span class="n">SecretClient</span> <span class="n">client</span><span class="p">,</span> 
            <span class="kt">string</span> <span class="n">secretName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">secretValue</span><span class="p">,</span> <span class="n">DateTime</span> <span class="n">expiresOn</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// Step 2: Get the current secret</span>
    <span class="kt">var</span> <span class="n">originalSecretResponse</span> <span class="p">=</span> <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="nf">GetSecretAsync</span><span class="p">(</span><span class="n">secretName</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">originalSecret</span> <span class="p">=</span> <span class="n">originalSecretResponse</span><span class="p">.</span><span class="n">Value</span><span class="p">;</span>
    
    <span class="c1">// Step 3: Disable the old secret</span>
    <span class="c1">// Set the old secret to disabled</span>
    <span class="n">originalSecret</span><span class="p">.</span><span class="n">Properties</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
    <span class="kt">var</span> <span class="n">updatePropertiesResponse</span> <span class="p">=</span> 
        <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="nf">UpdateSecretPropertiesAsync</span><span class="p">(</span><span class="n">originalSecret</span><span class="p">.</span><span class="n">Properties</span><span class="p">);</span>
    
    <span class="c1">// Step 4: Create a new version of the secret</span>
    <span class="c1">// Update secret value (create a new version)</span>
    <span class="kt">var</span> <span class="n">newSecretVersionResponse</span> <span class="p">=</span> <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="nf">SetSecretAsync</span><span class="p">(</span><span class="n">secretName</span><span class="p">,</span> <span class="n">secretValue</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">newKeyVaultSecretVersion</span> <span class="p">=</span> <span class="n">newSecretVersionResponse</span><span class="p">.</span><span class="n">Value</span><span class="p">;</span>
    
    <span class="c1">// Step 5: Update the expiration date</span>
    <span class="c1">// Update the expiration date</span>
    <span class="n">newKeyVaultSecretVersion</span><span class="p">.</span><span class="n">Properties</span><span class="p">.</span><span class="n">ExpiresOn</span> <span class="p">=</span> <span class="n">expiresOn</span><span class="p">;</span>
    <span class="n">updatePropertiesResponse</span> <span class="p">=</span> 
        <span class="k">await</span> <span class="n">client</span><span class="p">.</span><span class="nf">UpdateSecretPropertiesAsync</span><span class="p">(</span><span class="n">newKeyVaultSecretVersion</span><span class="p">.</span><span class="n">Properties</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice">The error handling in this code was removed for brevity.  You should add error handling to your code. You can find the full code in the <a href="https://github.com/jguadagno/jjgnet-broadcast/blob/a16b3f6dabc19b3ab60f1dacee23736d51b9adb3/src/JosephGuadagno.Broadcasting.Functions/Facebook/RefreshTokens.cs#L110" target="_blank">jjgnet-broadcasting</a> repository.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>I’m not sure why these steps are necessary to update a secret and its properties.  I would think that the <code class="language-plaintext highlighter-rouge">SetSecretAsync</code> method would update the secret and its properties with an overloaded method and/or better yet add an option to the <code class="language-plaintext highlighter-rouge">SetSecretAsync</code> method to disable the previous version, if one exists.  However, it doesn’t. I hope this post helps you if you need to update a secret and its properties in Azure Key Vault.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://docs.microsoft.com/en-us/azure/key-vault/key-vault-overview?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault</a></li>
  <li><a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretclient?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault SDK - SecretClient Class</a></li>
  <li><a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretclient.getsecretasync?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault SDK - SecretClient.GetSecretAsync Method</a></li>
  <li><a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretclient.setsecretasync?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault SDK - SecretClient.SetSecretAsync Method</a></li>
  <li><a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretproperties?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault SDK - SecretProperties Class</a></li>
  <li><a href="https://learn.microsoft.com/en-us/dotnet/api/azure.security.keyvault.secrets.secretclient.updatesecretproperties?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault SDK - SecretClient.UpdateSecretPropertiesMethod</a></li>
  <li><a href="https://azuresdkdocs.blob.core.windows.net/$web/dotnet/Azure.Security.KeyVault.Secrets/4.6.0/api/index.html#create-a-secret-asynchronously?wt.mc_id=DT-MVP-4024623" target="_blank">Azure.Security.KeyVault.Secrets Documentation</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Key Vault" /><category term=".NET" /><summary type="html"><![CDATA[When you use the Azure Key Vault SDK, you can create, read, update, and delete secrets. In this post, you will learn how to update a secret and its properties using the Azure Key Vault SDK.]]></summary></entry><entry><title type="html">Migrate to GitHub Actions from Pages Legacy Worker</title><link href="https://www.josephguadagno.net/2024/04/03/migrate-to-github-actions-from-pages-legacy-worker" rel="alternate" type="text/html" title="Migrate to GitHub Actions from Pages Legacy Worker" /><published>2024-04-03T16:45:00-07:00</published><updated>2024-04-03T16:45:00-07:00</updated><id>https://www.josephguadagno.net/2024/04/03/migrate-to-github-actions-from-pages-legacy-worker</id><content type="html" xml:base="https://www.josephguadagno.net/2024/04/03/migrate-to-github-actions-from-pages-legacy-worker"><![CDATA[<p>GitHub Pages has deprecated the legacy worker and will be removed by the end of June 2024. The legacy worker was used to build and deploy Jekyll sites on GitHub Pages. The new way to build and deploy Jekyll sites on GitHub Pages is to use GitHub Actions. This post will show you how to migrate your Jekyll site from the legacy worker to GitHub Actions.</p>

<h2 id="are-you-using-the-legacy-worker">Are you using the legacy worker?</h2>

<p>If you received an email from GitHub titled, “<strong><em>Important Announcement Re: Pages Legacy Worker Sunset</em></strong>”, you are likely using GitHub Pages with the legacy worker. If you are unsure if you are using the legacy worker you can check your repository settings.</p>

<ul>
  <li>Go to the repository settings, click on the “Pages” tab, scroll down to “Build and Deployment”, the <em>Source</em> would be set to <code class="language-plaintext highlighter-rouge">Deploy from a branch</code> similar to the image below</li>
</ul>

<p><img src="/assets/images/posts/2024/migrate-to-github-actions/migrate-to-github-actions-source.png" alt="Build and Deployment" /></p>

<p>If you already migrated to GitHub Actions, you will see the <code class="language-plaintext highlighter-rouge">Source</code> set to <code class="language-plaintext highlighter-rouge">GitHub Actions</code> and you should have a workflow file in the <code class="language-plaintext highlighter-rouge">.github\workflows</code> folder.</p>

<h2 id="migrate-to-github-actions">Migrate to GitHub Actions</h2>

<p>To migrate to GitHub Actions, you will need to create a workflow file in the <code class="language-plaintext highlighter-rouge">.github\workflows</code> folder. The workflow file will build and deploy your Jekyll site to GitHub Pages. Below is an example of a workflow file that builds and deploys a Jekyll site to GitHub Pages.</p>

<p>Navigate to the <em>Settings</em> page of your repository, click on the <em>Pages</em> tab, and scroll to <em>Build and deployment. Change the *Source</em> to <code class="language-plaintext highlighter-rouge">GitHub Actions</code>.  The page should update with two options, <code class="language-plaintext highlighter-rouge">GitHub Page Jekyll</code> and <code class="language-plaintext highlighter-rouge">Static HTML</code>. Choose the appropriate option for your site.</p>

<p><img src="/assets/images/posts/2024/migrate-to-github-actions/migrate-to-github-actions-template.png" alt="GitHub Action Template" /></p>

<p>If you are using Jekyll, choose the <code class="language-plaintext highlighter-rouge">GitHub Page Jekyll</code> option. If you are using static HTML, choose the <code class="language-plaintext highlighter-rouge">Static HTML</code> option. The templates are below.  If you do not need to modify the template you can click on <em>Commit changes</em> and GitHub will create the workflow file for you.</p>

<h3 id="jekyll-workflow-template">Jekyll Workflow Template</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
</pre></td><td class="rouge-code"><pre><span class="c1"># Sample workflow for building and deploying a Jekyll site to GitHub Pages</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">Deploy Jekyll with GitHub Pages dependencies preinstalled</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># Runs on pushes targeting the default branch</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">main"</span><span class="pi">]</span>

  <span class="c1"># Allows you to run this workflow manually from the Actions tab</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>

<span class="c1"># Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages</span>
<span class="na">permissions</span><span class="pi">:</span>
  <span class="na">contents</span><span class="pi">:</span> <span class="s">read</span>
  <span class="na">pages</span><span class="pi">:</span> <span class="s">write</span>
  <span class="na">id-token</span><span class="pi">:</span> <span class="s">write</span>

<span class="c1"># Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.</span>
<span class="c1"># However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.</span>
<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s2">"</span><span class="s">pages"</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="no">false</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># Build job</span>
  <span class="na">build</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Pages</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/configure-pages@v5</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build with Jekyll</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/jekyll-build-pages@v1</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">source</span><span class="pi">:</span> <span class="s">./</span>
          <span class="na">destination</span><span class="pi">:</span> <span class="s">./_site</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload artifact</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/upload-pages-artifact@v3</span>

  <span class="c1"># Deployment job</span>
  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">github-pages</span>
      <span class="na">url</span><span class="pi">:</span> <span class="s">$</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">needs</span><span class="pi">:</span> <span class="s">build</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to GitHub Pages</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">deployment</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/deploy-pages@v4</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="static-html-workflow-template">Static HTML Workflow Template</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
</pre></td><td class="rouge-code"><pre><span class="c1"># Simple workflow for deploying static content to GitHub Pages</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">Deploy static content to Pages</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="c1"># Runs on pushes targeting the default branch</span>
  <span class="na">push</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">main"</span><span class="pi">]</span>

  <span class="c1"># Allows you to run this workflow manually from the Actions tab</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>

<span class="c1"># Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages</span>
<span class="na">permissions</span><span class="pi">:</span>
  <span class="na">contents</span><span class="pi">:</span> <span class="s">read</span>
  <span class="na">pages</span><span class="pi">:</span> <span class="s">write</span>
  <span class="na">id-token</span><span class="pi">:</span> <span class="s">write</span>

<span class="c1"># Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.</span>
<span class="c1"># However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.</span>
<span class="na">concurrency</span><span class="pi">:</span>
  <span class="na">group</span><span class="pi">:</span> <span class="s2">"</span><span class="s">pages"</span>
  <span class="na">cancel-in-progress</span><span class="pi">:</span> <span class="no">false</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="c1"># Single deploy job since we're just deploying</span>
  <span class="na">deploy</span><span class="pi">:</span>
    <span class="na">environment</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">github-pages</span>
      <span class="na">url</span><span class="pi">:</span> <span class="s">$</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup Pages</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/configure-pages@v5</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload artifact</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/upload-pages-artifact@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="c1"># Upload entire repository</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s1">'</span><span class="s">.'</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Deploy to GitHub Pages</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">deployment</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/deploy-pages@v4</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="building-your-site">Building your site</h3>

<p>Based on the default workflow templates, GitHub Actions will build your site on pushes to the default branch (<code class="language-plaintext highlighter-rouge">main</code>). If you need to build your site on pushes to other branches, you can modify the <code class="language-plaintext highlighter-rouge">branches</code> key in the <code class="language-plaintext highlighter-rouge">on</code> section of the workflow file.</p>

<h2 id="using-jekyll-and-remote-themes">Using Jekyll and Remote Themes</h2>

<p>If you are using Jekyll with remote theme, and the remote theme is specified in the <code class="language-plaintext highlighter-rouge">_config.yml</code> file, you will need to add the GitHub Token to the workflow file in order to load the theme during the build. The GitHub Token is required to access GitHub from your action and use the remote theme.  If you do not have a GitHub token when your site is build you will a warning during the build process</p>

<p class="notice--warning">GitHub Metadata: No GitHub API authentication could be found. Some fields may be missing or have incorrect data.</p>

<p>Your site will still build but it will not look like you intended.</p>

<h3 id="getting-the-github-token">Getting the GitHub Token</h3>

<ol>
  <li>Sign in to GitHub and in the upper right hand corner click on your profile picture and select <em>Settings</em>.</li>
  <li>In the left sidebar, click on <em>Developer Settings</em>.</li>
  <li>In the left sidebar, click on <em>Personal access tokens</em>.</li>
  <li>Click <em>Tokens (Classic)</em>.</li>
  <li>Click on <em>Generate new token (classic)</em>.</li>
  <li>Authenticate, if required.</li>
  <li>For the note, enter a descriptive name for the token. Example: <code class="language-plaintext highlighter-rouge">JEKYLL_GITHUB_TOKEN</code>.</li>
  <li>Under <code class="language-plaintext highlighter-rouge">repo</code>, select <code class="language-plaintext highlighter-rouge">public_repo</code></li>
  <li>Click <em>Generate token</em>.</li>
</ol>

<p>Copy this token and store it in a secure location. You will need it later and you can not get it back once you leave the page.</p>

<p>Navigate back to the settings of your repository. Click on the <em>Secrets and variables</em> tab. Click on <em>Actions</em>. Click on <em>New repository secret</em>. Enter the following information:</p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
    </tr>
    <tr>
      <td>Name</td>
      <td><code class="language-plaintext highlighter-rouge">JEKYLL_GITHUB_TOKEN</code> <em>or something similar</em></td>
    </tr>
    <tr>
      <td>Secret</td>
      <td><em>Paste the token you copied earlier</em></td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Click <em>Add Secret</em></li>
</ul>

<h3 id="add-github-token-to-the-workflow-file">Add GitHub Token to the Workflow File</h3>

<p>Now we need to update the workflow file to use the token.</p>

<p>Edit the workflow file in your favorite editor to add the token to the environment.  If you used the default template, look in the <code class="language-plaintext highlighter-rouge">build</code> task for the <em>step</em> of <code class="language-plaintext highlighter-rouge">Build with Jekyll</code>, it should be around line 45. For this step we need to add an addition <code class="language-plaintext highlighter-rouge">env</code> key for the token.  The updated step should look like the following:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre>      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Build with Jekyll</span>
        <span class="c1"># Outputs to the './_site' directory by default</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">bundle exec jekyll build --baseurl "$"</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">JEKYLL_ENV</span><span class="pi">:</span> <span class="s">production</span>
          <span class="na">JEKYLL_GITHUB_TOKEN</span><span class="pi">:</span> <span class="s">${{ secrets.JEKYLL_GITHUB_TOKEN }}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>Replace <code class="language-plaintext highlighter-rouge">secrets.JEKYLL_GITHUB_TOKEN</code> with the name you used when you added the secret in the previous step.</li>
</ul>

<p>Save and commit the changes to your repository.  Once saved, the workflow will run and build your site.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>This should be all you need to migrate a GitHub Pages site from the legacy worker to GitHub Actions. If you have any questions or need help, please feel free to reach out to me on <a href="https://twitter.com/jguadagno" target="_blank">Twitter</a>.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://docs.github.com/en/actions" target="_blank">GitHub Actions Documentation</a></li>
  <li><a href="https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/about-github-pages-and-jekyll" target="_blank">GitHub Pages and Jekyll</a></li>
  <li><a href="https://docs.github.com/en/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages" target="_blank">Using custom workflows with GitHub pages</a></li>
  <li><a href="https://github.com/jguadagno/jguadagno.github.io/blob/main/.github/workflows/jekyll.yml" target="_blank">GitHub Workflow Action for JosephGuadagno.NET</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="GitHub" /><category term="Jekyll" /><category term="GitHub Actions" /><summary type="html"><![CDATA[GitHub Pages has deprecated the legacy worker and will be removed by the end of June 2024. The legacy worker was used to build and deploy Jekyll sites on GitHub Pages. The new way to build and deploy Jekyll sites on GitHub Pages is to use GitHub Actions. This post will show you how to migrate your Jekyll site from the legacy worker to GitHub Actions.]]></summary></entry><entry><title type="html">Working with the LinkedIn API with C#</title><link href="https://www.josephguadagno.net/2023/08/16/working-with-the-linkedin-api-with-c-sharp" rel="alternate" type="text/html" title="Working with the LinkedIn API with C#" /><published>2023-08-16T15:50:00-07:00</published><updated>2023-09-10T15:29:00-07:00</updated><id>https://www.josephguadagno.net/2023/08/16/working-with-the-linkedin-api-with-c-sharp</id><content type="html" xml:base="https://www.josephguadagno.net/2023/08/16/working-with-the-linkedin-api-with-c-sharp"><![CDATA[<p>The last day few days, I have been experimenting with the <a href="https://learn.microsoft.com/en-us/linkedin/consumer/?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn API</a> to send my blog posts, <a href="https://www.youtube.com/playlist?list=PLESGit66MnbmnDhXdTZicVFF2Pql430pJ" target="_blank">YouTube videos</a>, and community updates automatically to my <a href="https://www.linkedin.com/in/josephguadagno/" target="_blank">LinkedIn profile</a> from my open-source project <a href="https://github.com/jguadagno/jjgnet-broadcast/" target="_blank">JosephGuadagnoNet.Broadcasting</a>.  It’s been quite a challenge to get it to work.  I ran into authentication issues, permission issues, and serialization issues.  So what did I decide to do?  You guessed, solve the issues and blog about it so you don’t have to.</p>

<h2 id="getting-started">Getting Started</h2>

<p>First things first, you need to have a LinkedIn account and LinkedIn company page to post shares, as they are called on LinkedIn. If you don’t have an account or a company page, you can create one at <a href="https://www.linkedin.com/" target="_blank">LinkedIn</a>.  Once you have an account, you will need to create an application in the <a href="https://www.linkedin.com/developers/" target="_blank">LinkedIn Developer Portal</a>.</p>

<h3 id="developer-portal">Developer Portal</h3>

<p>On the Developer Portal, you will need to create an application.  You can do this by clicking on the <code class="language-plaintext highlighter-rouge">Create App</code> button in the center on of the page.  You will need to fill out the form with the following information:</p>

<table>
  <thead>
    <tr>
      <th>Field</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>App name *</td>
      <td>The name of your application</td>
    </tr>
    <tr>
      <td>LinkedIn Page *</td>
      <td>This is the owner (company page) of the application</td>
    </tr>
    <tr>
      <td>Privacy policy URL</td>
      <td>The URL to your privacy policy</td>
    </tr>
    <tr>
      <td>App logo *</td>
      <td>The logo for your application</td>
    </tr>
    <tr>
      <td>Legal agreement</td>
      <td>Read and accept that API Terms of Use</td>
    </tr>
  </tbody>
</table>

<p>(*) Required</p>

<p>Once done, you should see a screen similar to the following:</p>

<p><img src="/assets/images/posts/2023/linkedin-api-app-settings.png" alt="LinkedIn AI App Settings" class="align-center" /></p>

<h3 id="client-id-and-client-secret">Client ID and Client Secret</h3>

<p>Once you have an application, you will need to get the <code class="language-plaintext highlighter-rouge">clientid</code> and <code class="language-plaintext highlighter-rouge">clientsecret</code> for the application.  You will need these to authenticate with the LinkedIn API.  If you are going to be posting on behalf of a user, you will need to get an OAuth token for the user.  If you are going to be posting on behalf of a company, you will need to get an OAuth token for the company.  I won’t be covering the organizational OAuth token in this post.</p>

<p>Navigate to the <a href="https://www.linkedin.com/developers/tools/oauth" target="_blank">OAuth 2.0 Tools</a> page to begin.  You can also get there from you App Information page by clicking on <em>Docs and tools</em>, then <em>OAuth Token Tools</em>.</p>

<p><img src="/assets/images/posts/2023/linkedin-api-app-oauth-2-page.png" alt="LinkedIn AI OAuth 2.0 Tools page" class="align-center" /></p>

<ul>
  <li>Click on <em>Create token</em></li>
</ul>

<p>On the next page, you will need to select your application and either chose <em>Member authorization code (3-legged)</em> or <em>Client credential (2-legged)</em>. I didn’t come up with the names.  Basically, <em>Member authorization</em> is if you are going to be posting on behalf of the user, as I will in this post, and <em>Client credential</em> is if you are going to be building an application or interface where the user will be signing in and doing something with their data.  For this post, I will be using <em>Member authorization code (3-legged)</em>.</p>

<p class="notice">For more on the different types of OAuth 2.0 flows, see <a href="https://learn.microsoft.com/en-us/linkedin/shared/authentication/authentication?context=linkedin%2Fconsumer%2Fcontext&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn Authentication Overview</a>.</p>

<p>You will need to select the scopes that you will need for your application.  For this post, I will be using the following scopes:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">r_liteprofile</code>: Read basic profile information</li>
  <li><code class="language-plaintext highlighter-rouge">w_member_social</code>: Post, comment and like posts on behalf of the user</li>
</ul>

<p>Click <em>Request access token</em> to continue.</p>

<p>Once you do this, you will be redirected to sign-in to LinkedIn to grant the access. Enter your credentials and click <em>Sign In</em>.</p>

<p>Now, you will have a screen with the access token and details about the token.  You will need this access token to make calls to the LinkedIn API.  You can copy the token by clicking on the <em>Copy access token</em> button.</p>

<p><img src="/assets/images/posts/2023/linkedin-api-app-oauth-token.png" alt="LinkedIn AI OAuth 2.0 Tools page" class="align-center" /></p>

<p class="notice--warning">I strongly recommend that you save this access token somewhere secure like <a href="https://azure.microsoft.com/en-us/products/key-vault/?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault</a>. If someone gets a hold of it, they can post on behalf of the user.  If you do lose it, you can always revoke it and create a new one.</p>

<p>You’ll notice that the access token expires in approximately two months.  You will need to refresh the token before it expires. You will need to do this before the token expires. Details on how to do this are in the <a href="https://learn.microsoft.com/en-us/linkedin/shared/authentication/authentication?context=linkedin%2Fconsumer%2Fcontext&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn Authentication Overview</a>.  I won’t be covering that in this post.</p>

<h2 id="linkedin-manager">LinkedIn Manager</h2>

<p>Now that we have the access token, we can start posting to LinkedIn.  I created a class called <code class="language-plaintext highlighter-rouge">LinkedInManager</code> to handle all of the calls to the LinkedIn API. You check get the source in the <a href="https://github.com/jguadagno/linkedin-api-manager" target="_blank">LinkedIn API Manager GitHub Repository</a>.  I will be using the <a href="https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api?context=linkedin/consumer/context&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn API</a> to get the user’s LinkedIn ID and the <a href="https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/share-on-linkedin?wt.mc_id=DT-MVP-4024623" target="_blank">Share on LinkedIn API</a> to post the share.</p>

<h3 id="get-user-info">Get User Info</h3>

<p>All of the API calls to post a share on LinkedIn require the user’s LinkedIn ID.  You can get this by making a call to the <a href="https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api?context=linkedin/consumer/context&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Profile API</a>.  You will need to make a <code class="language-plaintext highlighter-rouge">GET</code> call to the <code class="language-plaintext highlighter-rouge">me</code> endpoint, as shown below as an http request.</p>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="err">GET https://api.linkedin.com/v2/me
Authorization: Bearer {{my-access-token}}
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>In this http request, as well as the future requests in this posts, you will need to replace <code class="language-plaintext highlighter-rouge">{{my-access-token}}</code> with the access token you received from the OAuth 2.0 Tools page. There will likely be another variable in future https requests, <code class="language-plaintext highlighter-rouge">{{my-person-id}}</code>.  This will be the LinkedIn ID you get from the <code class="language-plaintext highlighter-rouge">me</code> endpoint.</p>

<p>In the <code class="language-plaintext highlighter-rouge">LinkedInManager</code> class, I created a method called <code class="language-plaintext highlighter-rouge">GetUser</code> to make this call.  The method is shown below.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre> <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">LinkedInUser</span><span class="p">&gt;</span> <span class="nf">GetMyLinkedInUserProfile</span><span class="p">(</span><span class="kt">string</span> <span class="n">accessToken</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">accessToken</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentNullException</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">accessToken</span><span class="p">));</span>
    <span class="p">}</span>
    
    <span class="k">return</span> <span class="k">await</span> <span class="n">ExecuteGetAsync</span><span class="p">&lt;</span><span class="n">LinkedInUser</span><span class="p">&gt;(</span><span class="n">LinkedInUserUrl</span><span class="p">,</span> <span class="n">accessToken</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">ExecuteGetAsync</code> method is a helper method that makes the call to the LinkedIn API.  The method is shown below.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="rouge-code"><pre><span class="k">private</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;</span> <span class="n">ExecuteGetAsync</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="kt">string</span> <span class="n">url</span><span class="p">,</span> <span class="kt">string</span> <span class="n">accessToken</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">_httpClient</span><span class="p">.</span><span class="n">DefaultRequestHeaders</span><span class="p">.</span><span class="n">Accept</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">MediaTypeWithQualityHeaderValue</span><span class="p">(</span><span class="s">"application/json"</span><span class="p">));</span>
    <span class="n">_httpClient</span><span class="p">.</span><span class="n">DefaultRequestHeaders</span><span class="p">.</span><span class="nf">Add</span> <span class="p">(</span><span class="s">"Authorization"</span><span class="p">,</span> <span class="s">$"Bearer </span><span class="p">{</span><span class="n">accessToken</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_httpClient</span><span class="p">.</span><span class="nf">GetAsync</span><span class="p">(</span><span class="n">url</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">StatusCode</span> <span class="p">!=</span> <span class="n">HttpStatusCode</span><span class="p">.</span><span class="n">OK</span><span class="p">)</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">HttpRequestException</span><span class="p">(</span>
            <span class="s">$"Invalid status code in the HttpResponseMessage: </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">StatusCode</span><span class="p">}</span><span class="s">."</span><span class="p">);</span>
        
    <span class="c1">// Parse the Results</span>
    <span class="kt">var</span> <span class="n">content</span> <span class="p">=</span> <span class="k">await</span> <span class="n">response</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">ReadAsStringAsync</span><span class="p">();</span>
            
    <span class="kt">var</span> <span class="n">options</span> <span class="p">=</span> <span class="k">new</span> <span class="n">JsonSerializerOptions</span>
    <span class="p">{</span>
        <span class="n">PropertyNameCaseInsensitive</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
    <span class="p">};</span>

    <span class="kt">var</span> <span class="n">results</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">T</span><span class="p">&gt;(</span><span class="n">content</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">results</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">HttpRequestException</span><span class="p">(</span>
            <span class="s">$"Unable to deserialize the response from the HttpResponseMessage: </span><span class="p">{</span><span class="n">content</span><span class="p">}</span><span class="s">."</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">results</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You’ll notice that I am using the <code class="language-plaintext highlighter-rouge">HttpClient</code> class to make the call to the LinkedIn API.  I am also using the <code class="language-plaintext highlighter-rouge">System.Text.Json</code> library to deserialize the response from the API call.  You can use any library you want to make the call and deserialize the response.</p>

<p>On line 3 and 4, the required headers are set.  The <code class="language-plaintext highlighter-rouge">Accept</code> header is set to <code class="language-plaintext highlighter-rouge">application/json</code> and the <code class="language-plaintext highlighter-rouge">Authorization</code> header is set to the access token.</p>

<p>Line 5 makes the call to the LinkedIn API.  If the response is not <code class="language-plaintext highlighter-rouge">OK</code>, an exception is thrown.</p>

<p>Line 11 reads the response from the HTTP call.</p>

<p>Line 18 deserializes the response from the API call.  If the response is <code class="language-plaintext highlighter-rouge">null</code>, an exception is thrown.</p>

<p>Upon success, the <code class="language-plaintext highlighter-rouge">LinkedInUser</code> object is returned.  The <code class="language-plaintext highlighter-rouge">LinkedInUser</code> object is shown below.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">LinkedInUser</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"id"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Id</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>

    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"profilePicture"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="n">ProfilePicture</span> <span class="n">ProfilePicture</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"vanityName"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">VanityName</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"localizedFirstName"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">FirstName</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"localizedLastName"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">LastName</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"localizedHeadline"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">Headline</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"firstName"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="n">LocalizedInformation</span> <span class="n">LocalizedFirstName</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"lastName"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="n">LocalizedInformation</span> <span class="n">LocalizedLastName</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"headline"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="n">LocalizedInformation</span> <span class="n">LocalizedHeadline</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>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Note: Not all of these fields will be filled.  What is filled is based on the scope of for your OAuth token.  The <code class="language-plaintext highlighter-rouge">Id</code> property is the only property that we use.  Note, if you have the <code class="language-plaintext highlighter-rouge">r_liteprofile</code> scope, you will get all of the properties but <code class="language-plaintext highlighter-rouge">VanityName</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">Id</code> property of the <code class="language-plaintext highlighter-rouge">LinkedInUser</code> object, as well as other identifiers in the API, follows the Universal Resource Name <a href="http://www.ietf.org/rfc/rfc2141.txt" target="blank">URN</a> internet standard.  The format is <code class="language-plaintext highlighter-rouge">urn:li:person:&lt;person-id&gt;</code>, where <code class="language-plaintext highlighter-rouge">&lt;person-id&gt;</code> is the identifier for the person.  For most calls, you will need the full URN. In the <code class="language-plaintext highlighter-rouge">LinkedInManager</code> class, you only need the <code class="language-plaintext highlighter-rouge">&lt;person-id&gt;</code> portion of the URN.</p>

<p class="notice--info">For more on the URNs and IDs in LinkedIn, see the <a href="https://learn.microsoft.com/en-us/linkedin/shared/api-guide/concepts/urns?context=linkedin%2Fconsumer%2Fcontext&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn URNs and IDs</a> page.</p>

<h3 id="share-on-linkedin">Share on LinkedIn</h3>

<p>The <a href="https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/share-on-linkedin?context=linkedin%2Fconsumer%2Fcontext&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Share API</a> on LinkedIn provides the different ways to create shares or posts on LinkedIn; plain text, text with a link, and text with an image. I will show you how to create each of these types of shares.</p>

<h4 id="post-plain-text">Post Plain Text</h4>

<p>To post plain text, you will need to make a <code class="language-plaintext highlighter-rouge">POST</code> call to the <code class="language-plaintext highlighter-rouge">ugcPosts</code> endpoint.  The call to the <code class="language-plaintext highlighter-rouge">ugcPosts</code> endpoint is shown below.</p>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="rouge-code"><pre><span class="err">POST https://api.linkedin.com/v2/ugcPosts
Authorization: Bearer {{my-access-token}}
X-Restli-Protocol-Version: 2.0.0

{
  "author": "urn:li:person:{{my-person-id}}",
  "lifecycleState": "PUBLISHED",
  "specificContent": {
    "com.linkedin.ugc.ShareContent": {
      "shareCommentary": {
        "text": "Please ignore this post. This is a test post. It will be deleted shortly.}"
      },
      "shareMediaCategory": "NONE"
    }
  },
  "visibility": {
    "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
  }
}
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>A few things to note about this call. We introduced a new header <code class="language-plaintext highlighter-rouge">X-Restli-Protocol-Version</code>.  This header is required for all <code class="language-plaintext highlighter-rouge">POST</code> calls.  The value of the header is <code class="language-plaintext highlighter-rouge">2.0.0</code>.</p>

<p>In the LinkedIn API Manager, as call is made to <code class="language-plaintext highlighter-rouge">PostShareText</code>.  The method is shown below.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">PostShareText</span><span class="p">(</span><span class="kt">string</span> <span class="n">accessToken</span><span class="p">,</span> <span class="kt">string</span> <span class="n">authorId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">postText</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// Validation removed for brevity</span>
    <span class="kt">var</span> <span class="n">shareRequest</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ShareRequest</span>
    <span class="p">{</span>
        <span class="n">Author</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="n">LinkedInAuthorUrn</span><span class="p">,</span> <span class="n">authorId</span><span class="p">),</span>
        <span class="n">Visibility</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Visibility</span> <span class="p">{</span> <span class="n">VisibilityEnum</span> <span class="p">=</span> <span class="n">VisibilityEnum</span><span class="p">.</span><span class="n">Anyone</span> <span class="p">},</span>
        <span class="n">SpecificContent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SpecificContent</span>
        <span class="p">{</span>
            <span class="n">ShareContent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ShareContent</span>
            <span class="p">{</span>
                <span class="n">ShareCommentary</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">TextProperties</span><span class="p">()</span>
                <span class="p">{</span>
                    <span class="n">Text</span> <span class="p">=</span> <span class="n">postText</span>
                <span class="p">},</span>
                <span class="n">ShareMediaCategoryEnum</span> <span class="p">=</span> <span class="n">ShareMediaCategoryEnum</span><span class="p">.</span><span class="n">None</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">};</span>
    
    <span class="kt">var</span> <span class="n">linkedInResponse</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">CallPostShareUrl</span><span class="p">(</span><span class="n">accessToken</span><span class="p">,</span> <span class="n">shareRequest</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">linkedInResponse</span> <span class="k">is</span> <span class="p">{</span> <span class="n">IsSuccess</span><span class="p">:</span> <span class="k">true</span><span class="p">,</span> <span class="n">Id</span><span class="p">:</span> <span class="n">not</span> <span class="k">null</span> <span class="p">})</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="n">linkedInResponse</span><span class="p">.</span><span class="n">Id</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nf">HttpRequestException</span><span class="p">(</span><span class="s">$"Failed to post status update to LinkedIn: LinkedIn Status Code: '</span><span class="p">{</span><span class="n">linkedInResponse</span><span class="p">.</span><span class="n">ServiceErrorCode</span><span class="p">}</span><span class="s">', LinkedIn Message: '</span><span class="p">{</span><span class="n">linkedInResponse</span><span class="p">.</span><span class="n">Message</span><span class="p">}</span><span class="s">'"</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>We start off by creating a <code class="language-plaintext highlighter-rouge">ShareRequest</code> object.  Now, in my implementation I have made a view assumption like setting the <code class="language-plaintext highlighter-rouge">Visibility</code> to <code class="language-plaintext highlighter-rouge">Anyone</code>. You might want to change that in your implementation.  Similar to the <code class="language-plaintext highlighter-rouge">GetMyLinkedInProfile</code> call, I use a helper method for all of the <code class="language-plaintext highlighter-rouge">POST</code> calls.  This was helpful for a few reasons, the first, I was repeated code, but more importantly, the serialization and deserialization of the request and response for the LinkedIn API was very particular.  The helper method is shown below.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
</pre></td><td class="rouge-code"><pre><span class="k">private</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ShareResponse</span><span class="p">&gt;</span> <span class="nf">CallPostShareUrl</span><span class="p">(</span><span class="kt">string</span> <span class="n">accessToken</span><span class="p">,</span> <span class="n">ShareRequest</span> <span class="n">shareRequest</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// Validation removed for brevity</span>
    
    <span class="n">HttpRequestMessage</span> <span class="n">requestMessage</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">HttpRequestMessage</span><span class="p">(</span><span class="n">HttpMethod</span><span class="p">.</span><span class="n">Post</span><span class="p">,</span> <span class="n">LinkedInPostUrl</span><span class="p">);</span>
    <span class="n">requestMessage</span><span class="p">.</span><span class="n">Headers</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="s">"Authorization"</span><span class="p">,</span> <span class="s">$"Bearer </span><span class="p">{</span><span class="n">accessToken</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
    <span class="n">requestMessage</span><span class="p">.</span><span class="n">Headers</span><span class="p">.</span><span class="nf">Add</span> <span class="p">(</span><span class="s">"X-Restli-Protocol-Version"</span><span class="p">,</span> <span class="s">"2.0.0"</span><span class="p">);</span>
    
    <span class="n">JsonSerializerOptions</span> <span class="n">jsonSerializationOptions</span> <span class="p">=</span> <span class="k">new</span><span class="p">(</span><span class="n">JsonSerializerDefaults</span><span class="p">.</span><span class="n">Web</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">WriteIndented</span> <span class="p">=</span> <span class="k">false</span><span class="p">,</span>
        <span class="n">DefaultIgnoreCondition</span> <span class="p">=</span> <span class="n">JsonIgnoreCondition</span><span class="p">.</span><span class="n">WhenWritingNull</span><span class="p">,</span>
    <span class="p">};</span>
    <span class="kt">var</span> <span class="n">jsonRequest</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">shareRequest</span><span class="p">,</span> <span class="n">jsonSerializationOptions</span><span class="p">);</span>  
    <span class="kt">var</span> <span class="n">jsonContent</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">StringContent</span><span class="p">(</span><span class="n">jsonRequest</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="s">"application/json"</span><span class="p">);</span>
    <span class="n">requestMessage</span><span class="p">.</span><span class="n">Content</span> <span class="p">=</span> <span class="n">jsonContent</span><span class="p">;</span>
    
    <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_httpClient</span><span class="p">.</span><span class="nf">SendAsync</span><span class="p">(</span><span class="n">requestMessage</span><span class="p">);</span>
    
    <span class="kt">var</span> <span class="n">content</span> <span class="p">=</span> <span class="k">await</span> <span class="n">response</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">ReadAsStringAsync</span><span class="p">();</span>
    <span class="kt">var</span> <span class="n">options</span> <span class="p">=</span> <span class="k">new</span> <span class="n">JsonSerializerOptions</span>
    <span class="p">{</span>
        <span class="n">PropertyNameCaseInsensitive</span> <span class="p">=</span> <span class="k">true</span>
    <span class="p">};</span>
    <span class="kt">var</span> <span class="n">linkedInResponse</span> <span class="p">=</span> <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">ShareResponse</span><span class="p">&gt;(</span><span class="n">content</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">linkedInResponse</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">// TODO: Custom Exception</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">HttpRequestException</span><span class="p">(</span>
            <span class="s">$"Unable to deserialize the response from the HttpResponseMessage: </span><span class="p">{</span><span class="n">content</span><span class="p">}</span><span class="s">."</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">linkedInResponse</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Lines 5-7, I prepare the http call and headers.</p>

<p>Line 9 - 16, I send the <code class="language-plaintext highlighter-rouge">JsonSerializationOptions</code>.  Take note of the <code class="language-plaintext highlighter-rouge">DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull</code> setting, without this calls to the LinkedIn API will fail with either a <code class="language-plaintext highlighter-rouge">400</code> or <code class="language-plaintext highlighter-rouge">422</code> error.</p>

<p>Line 18, I send the request.</p>

<p>Line 20 -25, I deserialize the response, and if successful, return the <code class="language-plaintext highlighter-rouge">ShareResponse</code> object.</p>

<p>The <code class="language-plaintext highlighter-rouge">ShareResponse</code> object, shown below, is used to deserialize the response from the LinkedIn API. Only the <code class="language-plaintext highlighter-rouge">Id</code> property is provided upon a successful call to the LinkedIn API. If the call fails, the <code class="language-plaintext highlighter-rouge">Message</code>, <code class="language-plaintext highlighter-rouge">ServiceErrorCode</code>, and <code class="language-plaintext highlighter-rouge">Status</code> properties will be populated.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">ShareResponse</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"message"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">string</span><span class="p">?</span> <span class="n">Message</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"serviceErrorCode"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">int</span><span class="p">?</span> <span class="n">ServiceErrorCode</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"status"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">int</span><span class="p">?</span> <span class="n">Status</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>
    
    <span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"id"</span><span class="p">)]</span>
    <span class="k">public</span> <span class="kt">string</span><span class="p">?</span> <span class="n">Id</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>
    
    <span class="k">public</span> <span class="kt">bool</span> <span class="n">IsSuccess</span> <span class="p">=&gt;</span> <span class="p">!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">Id</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You can do with the <code class="language-plaintext highlighter-rouge">Id</code> property as you like, this is LinkedIn’s unique identifier for the post.</p>

<h4 id="post-text-with-link">Post Text with Link</h4>

<p>The post text with link API is very similar to the post text API. The only difference is the addition of <code class="language-plaintext highlighter-rouge">media</code> object in the <code class="language-plaintext highlighter-rouge">com.linkedin.ugc.ShareContent</code> section of request. Here is the sample request.</p>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
</pre></td><td class="rouge-code"><pre><span class="err">POST https://api.linkedin.com/v2/ugcPosts
Authorization: Bearer {{my-access-token}}
X-Restli-Protocol-Version: 2.0.0

{
    "author": "urn:li:person:{{my-person-id}}",
    "lifecycleState": "PUBLISHED",
    "specificContent": {
        "com.linkedin.ugc.ShareContent": {
            "shareCommentary": {
                "text": "LinkedIn has an AI Assisted Editor for posting articles."
            },
            "shareMediaCategory": "ARTICLE",
            "media": [
                {
                    "status": "READY",
                    "description": {
                        "text": "This is the description of the media."
                    },
                    "originalUrl": "https://www.josephguadagno.net/",
                    "title": {
                        "text": "Joseph Guadagno Website"
                    }
                }
            ]
        }
    },
    "visibility": {
        "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
    }
}
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>In the LinkedIn API Manager, a call is made to the <code class="language-plaintext highlighter-rouge">PostTextWithLink</code> method. The code is shown below.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">PostShareTextAndLink</span><span class="p">(</span><span class="kt">string</span> <span class="n">accessToken</span><span class="p">,</span> <span class="kt">string</span> <span class="n">authorId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">postText</span><span class="p">,</span> <span class="kt">string</span> <span class="n">link</span><span class="p">,</span> <span class="kt">string</span><span class="p">?</span> <span class="n">linkTitle</span> <span class="p">=</span> <span class="k">null</span><span class="p">,</span> <span class="kt">string</span><span class="p">?</span> <span class="n">linkDescription</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// Validation removed for brevity</span>
    
    <span class="kt">var</span> <span class="n">shareRequest</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ShareRequest</span>
    <span class="p">{</span>
        <span class="n">Author</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="n">LinkedInAuthorUrn</span><span class="p">,</span> <span class="n">authorId</span><span class="p">),</span>
        <span class="n">Visibility</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Visibility</span> <span class="p">{</span> <span class="n">VisibilityEnum</span> <span class="p">=</span> <span class="n">VisibilityEnum</span><span class="p">.</span><span class="n">Anyone</span> <span class="p">},</span>
        <span class="n">SpecificContent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SpecificContent</span>
        <span class="p">{</span>
            <span class="n">ShareContent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ShareContent</span>
            <span class="p">{</span>
                <span class="n">ShareCommentary</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">TextProperties</span><span class="p">()</span>
                <span class="p">{</span>
                    <span class="n">Text</span> <span class="p">=</span> <span class="n">postText</span>
                <span class="p">},</span>
                <span class="n">ShareMediaCategoryEnum</span> <span class="p">=</span> <span class="n">ShareMediaCategoryEnum</span><span class="p">.</span><span class="n">Article</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">};</span>
    <span class="kt">var</span> <span class="n">media</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Media</span><span class="p">{</span><span class="n">OriginalUrl</span> <span class="p">=</span> <span class="n">link</span><span class="p">};</span>
    <span class="k">if</span> <span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">linkDescription</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">media</span><span class="p">.</span><span class="n">Description</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TextProperties</span> <span class="p">{</span><span class="n">Text</span> <span class="p">=</span> <span class="n">linkDescription</span><span class="p">};</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">linkTitle</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">media</span><span class="p">.</span><span class="n">Title</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TextProperties</span> <span class="p">{</span><span class="n">Text</span> <span class="p">=</span> <span class="n">linkTitle</span><span class="p">};</span>
    <span class="p">}</span>
    <span class="n">shareRequest</span><span class="p">.</span><span class="n">SpecificContent</span><span class="p">.</span><span class="n">ShareContent</span><span class="p">.</span><span class="n">Media</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="n">media</span> <span class="p">};</span>
    
    <span class="kt">var</span> <span class="n">linkedInResponse</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">CallPostShareUrl</span><span class="p">(</span><span class="n">accessToken</span><span class="p">,</span> <span class="n">shareRequest</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">linkedInResponse</span> <span class="k">is</span> <span class="p">{</span> <span class="n">IsSuccess</span><span class="p">:</span> <span class="k">true</span><span class="p">,</span> <span class="n">Id</span><span class="p">:</span> <span class="n">not</span> <span class="k">null</span> <span class="p">})</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="n">linkedInResponse</span><span class="p">.</span><span class="n">Id</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nf">HttpRequestException</span><span class="p">(</span><span class="nf">BuildLinkedInResponseErrorMessage</span><span class="p">(</span><span class="n">linkedInResponse</span><span class="p">));</span>

<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You’ll notice that most of the start of the code looks the same as the <code class="language-plaintext highlighter-rouge">PostShareText</code> method. However, on line 17 we see the <code class="language-plaintext highlighter-rouge">ShareMediaCategoryEnum</code> field to <code class="language-plaintext highlighter-rouge">ShareMediaCategoryEnum.Article</code> which creates a “Article”, a link with a posts.</p>

<p>On lines 21-30 we construct the <code class="language-plaintext highlighter-rouge">Media</code> object and add it to the <code class="language-plaintext highlighter-rouge">ShareContent</code> object.</p>

<p>Line 32, we call the <code class="language-plaintext highlighter-rouge">CallPostShareUrl</code> method and return the <code class="language-plaintext highlighter-rouge">Id</code> property if successful.</p>

<h4 id="post-text-with-image">Post Text with Image</h4>

<p>The post text with image API is very similar to the post text with link API.  The only different is that we have to upload the image to LinkedIn.  This is a three step process.  Step one, is for us to notify LinkedIn that we want to add file. I know, it’s weird but I didn’t write the API, luckily, it’s all wrapped in the LinkedIn API Manager.  Step two, is to upload the file to LinkedIn. Followed by step three, which is to post the share with the image.</p>

<h5 id="step-one---notify-linkedin-of-the-file">Step One - Notify LinkedIn of the File</h5>

<p>In order to upload an image to LinkedIn, we need to notify LinkedIn that we want to upload a file. This is done by calling the <code class="language-plaintext highlighter-rouge">asset</code> API and using the <code class="language-plaintext highlighter-rouge">registerUpload</code> method.  Here is the sample request.</p>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="rouge-code"><pre><span class="err">POST https://api.linkedin.com/v2/assets?action=registerUpload
Authorization: Bearer {{my-access-token}}
X-Restli-Protocol-Version: 2.0.0

{
    "registerUploadRequest": {
        "recipes": [
            "urn:li:digitalmediaRecipe:feedshare-image"
        ],
        "owner": "urn:li:person:{{my-person-id}}",
        "serviceRelationships": [
            {
                "relationshipType": "OWNER",
                "identifier": "urn:li:userGeneratedContent"
            }
        ]
    }
}
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>The only dynamic part of this request is the <code class="language-plaintext highlighter-rouge">owner</code> field.  This is the LinkedIn URN of the person who is uploading the image. Upon success, you will receive a response back similar to this</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
    </span><span class="nl">"value"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"uploadMechanism"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
                </span><span class="nl">"headers"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w">
                </span><span class="nl">"uploadUrl"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://api.linkedin.com/mediaUpload/&lt;LinkedInAssetId&gt;/feedshare-uploadedImage/0?ca=vector_feedshare&amp;cn=uploads&amp;m=AQJbrN86Zm265gAAAWemyz2pxPSgONtBiZdchrgG872QltnfYjnMdb2j3A&amp;app=1953784&amp;sync=0&amp;v=beta&amp;ut=2H-IhpbfXrRow1"</span><span class="w">
            </span><span class="p">}</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"mediaArtifact"</span><span class="p">:</span><span class="w"> </span><span class="s2">"urn:li:digitalmediaMediaArtifact:(urn:li:digitalmediaAsset:&lt;LinkedInAssetId&gt;,urn:li:digitalmediaMediaArtifactClass:feedshare-uploadedImage)"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"asset"</span><span class="p">:</span><span class="w"> </span><span class="s2">"urn:li:digitalmediaAsset:&lt;LinkedInAssetId&gt;"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">&lt;LinkedInAssetId&gt;</code> is a unique Asset Id that LinkedIn assigned. I replaced a real asset id in this response with <code class="language-plaintext highlighter-rouge">&lt;LinkedInAssetId&gt;</code> so no one deletes the object. :smile:</p>

<p>For the next step, we are going to need the <code class="language-plaintext highlighter-rouge">uploadUrl</code> from the response.  We will use this to upload the image to LinkedIn.  Will we also need the <code class="language-plaintext highlighter-rouge">asset</code> field for when we create the post on LinkedIn. Again, this is all wrapped up in the LinkedIn API Manager.</p>

<h5 id="step-two---upload-the-image">Step Two - Upload the Image</h5>

<p>For this, I don’t have an http request to show you, if you want to play around with an http client you will need to use something like <code class="language-plaintext highlighter-rouge">cUrl</code> to upload the image.  Sample <code class="language-plaintext highlighter-rouge">cUrl</code> command is shown below.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>curl <span class="nt">-i</span> <span class="nt">--upload-file</span> <span class="o">{{</span>path-to-image<span class="o">}}</span> <span class="nt">--header</span> <span class="s2">"Authorization: Bearer {{my-access-token}}"</span> <span class="s1">'{{uploadUrl}}'</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Replace the following tokens</p>

<table>
  <thead>
    <tr>
      <th>Token</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">{{path-to-image}}</code></td>
      <td>The path to the image you want to upload</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">{{my-access-token}}</code></td>
      <td>Your LinkedIn access token</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">{{uploadUrl}}</code></td>
      <td>The <code class="language-plaintext highlighter-rouge">uploadUrl</code> from the response of the <code class="language-plaintext highlighter-rouge">registerUpload</code> API call</td>
    </tr>
  </tbody>
</table>

<h5 id="step-three---post-the-share">Step Three - Post the Share</h5>

<p>For the http request, it is almost the same as the post text and link. We only change the <code class="language-plaintext highlighter-rouge">ShareMediaCategoryEnum</code> to <code class="language-plaintext highlighter-rouge">ShareMediaCategoryEnum.Image</code>.  Here is the sample request.</p>

<div class="language-http highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
</pre></td><td class="rouge-code"><pre><span class="err">POST https://api.linkedin.com/v2/ugcPosts
Authorization: Bearer {{my-access-token}}
X-Restli-Protocol-Version: 2.0.0

{
    "author": "urn:li:person:{{my-person-id}}",
    "lifecycleState": "PUBLISHED",
    "specificContent": {
        "com.linkedin.ugc.ShareContent": {
            "shareCommentary": {
                "text": "LinkedIn has an AI Assisted Editor for posting articles."
            },
            "shareMediaCategory": "IMAGE",
            "media": [
                {
                    "status": "READY",
                    "description": {
                        "text": "LinkedIn has an AI Assisted Editor for posting articles."
                    },
                    "media": "urn:li:digitalmediaAsset:D5622AQHqpGB5YNqcvg",
                    "originalUrl": "https://www.josephguadagno.net/2023/08/08/linkedin-now-has-an-ai-assisted-editor-for-post",
                    "title": {
                        "text": "LinkedIn has an AI Assisted Editor for Post"
                    }
                }
            ]
        }
    },
    "visibility": {
        "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
    }
}
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Mostly everything is the same as the previous call, however, here we set the <code class="language-plaintext highlighter-rouge">media</code> to the <code class="language-plaintext highlighter-rouge">asset</code> field we received when we made the upload request.</p>

<p>In the LinkedIn API Manager, the call looks like this.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">PostShareTextAndImage</span><span class="p">(</span><span class="kt">string</span> <span class="n">accessToken</span><span class="p">,</span> <span class="kt">string</span> <span class="n">authorId</span><span class="p">,</span> <span class="kt">string</span> <span class="n">postText</span><span class="p">,</span> <span class="kt">byte</span><span class="p">[]</span> <span class="n">image</span><span class="p">,</span> <span class="kt">string</span><span class="p">?</span> <span class="n">imageTitle</span> <span class="p">=</span> <span class="k">null</span><span class="p">,</span> <span class="kt">string</span><span class="p">?</span> <span class="n">imageDescription</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// Validation removed for brevity</span>
    
    <span class="c1">// Call the Register Image endpoint to get the Asset URN</span>
    <span class="kt">var</span> <span class="n">uploadResponse</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">GetUploadResponse</span><span class="p">(</span><span class="n">accessToken</span><span class="p">,</span> <span class="n">authorId</span><span class="p">);</span>
    
    <span class="c1">// Upload the image</span>
    <span class="kt">var</span> <span class="n">uploadUrl</span> <span class="p">=</span> <span class="n">uploadResponse</span><span class="p">.</span><span class="n">Value</span><span class="p">.</span><span class="n">UploadMechanism</span><span class="p">.</span><span class="n">MediaUploadHttpRequest</span><span class="p">.</span><span class="n">UploadUrl</span><span class="p">;</span>
    <span class="kt">var</span> <span class="n">wasFileUploadSuccessful</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">UploadImage</span><span class="p">(</span><span class="n">accessToken</span><span class="p">,</span> <span class="n">uploadUrl</span><span class="p">,</span> <span class="n">image</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(!</span><span class="n">wasFileUploadSuccessful</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">throw</span> <span class="k">new</span> <span class="nf">ApplicationException</span><span class="p">(</span><span class="s">"Failed to upload image to LinkedIn"</span><span class="p">);</span>
    <span class="p">}</span>
    
    <span class="c1">// Send the image via PostShare</span>
    <span class="kt">var</span> <span class="n">shareRequest</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ShareRequest</span>
    <span class="p">{</span>
        <span class="n">Author</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="n">LinkedInAuthorUrn</span><span class="p">,</span> <span class="n">authorId</span><span class="p">),</span>
        <span class="n">Visibility</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Visibility</span> <span class="p">{</span> <span class="n">VisibilityEnum</span> <span class="p">=</span> <span class="n">VisibilityEnum</span><span class="p">.</span><span class="n">Anyone</span> <span class="p">},</span>
        <span class="n">SpecificContent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SpecificContent</span>
        <span class="p">{</span>
            <span class="n">ShareContent</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ShareContent</span>
            <span class="p">{</span>
                <span class="n">ShareCommentary</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">TextProperties</span><span class="p">()</span>
                <span class="p">{</span>
                    <span class="n">Text</span> <span class="p">=</span> <span class="n">postText</span>
                <span class="p">},</span>
                <span class="n">ShareMediaCategoryEnum</span> <span class="p">=</span> <span class="n">ShareMediaCategoryEnum</span><span class="p">.</span><span class="n">Image</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">};</span>
    
    <span class="kt">var</span> <span class="n">media</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Media</span><span class="p">{</span><span class="n">MediaUrn</span> <span class="p">=</span> <span class="n">uploadResponse</span><span class="p">.</span><span class="n">Value</span><span class="p">.</span><span class="n">Asset</span><span class="p">};</span>
    
    <span class="k">if</span> <span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">imageDescription</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">media</span><span class="p">.</span><span class="n">Description</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TextProperties</span> <span class="p">{</span><span class="n">Text</span> <span class="p">=</span> <span class="n">imageDescription</span><span class="p">};</span>
    <span class="p">}</span>
    <span class="k">if</span> <span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">imageTitle</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">media</span><span class="p">.</span><span class="n">Title</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TextProperties</span> <span class="p">{</span><span class="n">Text</span> <span class="p">=</span> <span class="n">imageTitle</span><span class="p">};</span>
    <span class="p">}</span>
    <span class="n">shareRequest</span><span class="p">.</span><span class="n">SpecificContent</span><span class="p">.</span><span class="n">ShareContent</span><span class="p">.</span><span class="n">Media</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="n">media</span> <span class="p">};</span>
    
    <span class="kt">var</span> <span class="n">linkedInResponse</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">CallPostShareUrl</span><span class="p">(</span><span class="n">accessToken</span><span class="p">,</span> <span class="n">shareRequest</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">linkedInResponse</span> <span class="k">is</span> <span class="p">{</span> <span class="n">IsSuccess</span><span class="p">:</span> <span class="k">true</span><span class="p">,</span> <span class="n">Id</span><span class="p">:</span> <span class="n">not</span> <span class="k">null</span> <span class="p">})</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="n">linkedInResponse</span><span class="p">.</span><span class="n">Id</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nf">HttpRequestException</span><span class="p">(</span><span class="nf">BuildLinkedInResponseErrorMessage</span><span class="p">(</span><span class="n">linkedInResponse</span><span class="p">));</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>On line 6, the request is made to upload the file via the <code class="language-plaintext highlighter-rouge">GetUploadResponse</code> method, which returns a <code class="language-plaintext highlighter-rouge">UploadRegistrationResponse</code> upon success, otherwise it throws an exception.</p>

<p>On lines 9 and 10 we upload the file via the <code class="language-plaintext highlighter-rouge">UploadImage</code> method, which returns a <code class="language-plaintext highlighter-rouge">true</code> upon success, otherwise a failure.</p>

<p>The rest of code is the same as the previous example.</p>

<p>You can view this methods in the <a href="https://github.com/jguadagno/linkedin-api-manager/blob/main/src/LinkedIn/LinkedInManager.cs" target="_blank">LinkedInApiManager.cs</a> file.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>That was a lot to take in.  We covered how to post text, links, and images to LinkedIn.  We also covered how to get the access token. We also covered how to get the user’s profile information.  I hope you found this useful as well as the break down of calls required to call the API.</p>

<p>If you have any questions, please feel free to reach out to me on <a href="https://twitter.com/jguadagno" target="_blank">Twitter</a>, <a href="https://www.linkedin.com/in/josephguadagno/" target="_blank">LinkedIn</a>, or via the <a href="/contact/">contact</a>.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://developer.linkedin.com/" target="_blank">LinkedIn Developer Products</a></li>
  <li><a href="https://learn.microsoft.com/en-us/linkedin/shared/authentication/authentication?context=linkedin%2Fconsumer%2Fcontext&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn Authentication Overview</a></li>
  <li><a href="https://learn.microsoft.com/en-us/linkedin/consumer/?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn API</a></li>
  <li><a href="https://learn.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/share-on-linkedin?context=linkedin%2Fconsumer%2Fcontext&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn API Documentation - Share On LinkedIn</a></li>
  <li><a href="https://learn.microsoft.com/en-us/linkedin/shared/api-guide/concepts/urns?context=linkedin%2Fconsumer%2Fcontext&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn API Documentation - URNs and IDs</a></li>
  <li><a href="https://learn.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api?context=linkedin%2Fconsumer%2Fcontext&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn Api Documentation - Profile API</a></li>
  <li><a href="https://github.com/jguadagno/linkedin-api-manager" target="_blank">LinkedIn API Manager GitHub Repository</a></li>
  <li><a href="https://www.linkedin.com/in/josephguadagno/" target="_blank">My LinkedIn Profile</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="API" /><category term="LinkedIn" /><category term="csharp" /><category term=".NET" /><summary type="html"><![CDATA[The last day few days, I have been experimenting with the LinkedIn API to send my blog posts, YouTube videos, and community updates automatically to my LinkedIn profile from my open-source project JosephGuadagnoNet.Broadcasting. It’s been quite a challenge to get it to work. I ran into authentication issues, permission issues, and serialization issues. So what did I decide to do? You guessed, solve the issues and blog about it so you don’t have to.]]></summary></entry><entry><title type="html">Coding with JoeG - Reboot</title><link href="https://www.josephguadagno.net/2023/08/10/coding-with-joeg-reboot" rel="alternate" type="text/html" title="Coding with JoeG - Reboot" /><published>2023-08-10T16:35:00-07:00</published><updated>2023-08-10T16:35:00-07:00</updated><id>https://www.josephguadagno.net/2023/08/10/coding-with-joeg-reboot</id><content type="html" xml:base="https://www.josephguadagno.net/2023/08/10/coding-with-joeg-reboot"><![CDATA[<p>Tomorrow, August 11th, 2023, I will kick off my Coding with JoeG stream again and I’ll be joined by <a href="https://www.linkedin.com/in/ACoAAAAYFTQB8y3h2jQK_51y7RmIo3PSopBQxDw?" target="_blank">Richard Dudley</a>. We’ll start the stream at 1pm MST/PST, 4pm EST, 8pm UTC</p>

<p>Rich and I will work together to learn how to use <a href="https://kubernetes.io/" target="_blank">Kubernetes</a>. Here is the multi-day agenda:</p>

<h2 id="local-development">Local Development</h2>

<ul>
  <li>Demo the sample application</li>
  <li>Create a new repository for it</li>
  <li>Convert the application to use Containers</li>
  <li>Deploy to Kubernetes locally</li>
  <li>Figure out how to manage secrets</li>
  <li>Check stats of the cluster</li>
  <li>Look at Monitoring/Logging/Debugging</li>
</ul>

<h2 id="deploy-to-aws">Deploy to AWS</h2>

<ul>
  <li>Look at Cost</li>
  <li>Look at Deployment options</li>
  <li>Deploy to AWS</li>
  <li>Monitor</li>
</ul>

<h2 id="deploy-to-azure">Deploy to Azure</h2>

<ul>
  <li>Look at Cost</li>
  <li>Look at Deployment options</li>
  <li>Deploy to Azure</li>
  <li>Monitor</li>
</ul>

<h2 id="broadcasting-on">Broadcasting on</h2>

<ul>
  <li>Twitch: <a href="https://jjg.me/stream" target="_blank">https://jjg.me/stream</a></li>
  <li>YouTube: <a href="https://jjg.me/youtube" target="_blank">https://jjg.me/youtube</a></li>
  <li>Facebook: <a href="https://facebook.com/josephguadagnonet" target="_blank">https://facebook.com/josephguadagnonet</a></li>
</ul>

<p class="notice">At the moment, my company page on <a href="https://lnkd.in/gTwNYV-e" target="_blank">LinkedIn</a> does not have enough followers so I cannot broadcast there yet.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term="Twitch" /><category term="Stream" /><category term="CodingWithJoeG" /><category term="Archive" /><summary type="html"><![CDATA[Tomorrow, August 11th, 2023, I will kick off my Coding with JoeG stream again and I’ll be joined by Richard Dudley. We’ll start the stream at 1pm MST/PST, 4pm EST, 8pm UTC]]></summary></entry><entry><title type="html">LinkedIn has an AI Assisted Editor for Post</title><link href="https://www.josephguadagno.net/2023/08/08/linkedin-now-has-an-ai-assisted-editor-for-post" rel="alternate" type="text/html" title="LinkedIn has an AI Assisted Editor for Post" /><published>2023-08-08T20:45:00-07:00</published><updated>2023-08-08T20:45:00-07:00</updated><id>https://www.josephguadagno.net/2023/08/08/linkedin-now-has-an-ai-assisted-editor-for-post</id><content type="html" xml:base="https://www.josephguadagno.net/2023/08/08/linkedin-now-has-an-ai-assisted-editor-for-post"><![CDATA[<p>With me on the hunt for a new job, at the time of this post being authored, I have been pretty active on <a href="https://www.linkedin.com/in/josephguadagno/" target="_blank">LinkedIn</a>. The last day or so, I have been experimenting with the <a href="https://learn.microsoft.com/en-us/linkedin/consumer/?wt.mc_id=DT-MVP-4024623" target="_blank">LinkedIn API</a> to send my blog posts, <a href="https://www.youtube.com/playlist?list=PLESGit66MnbmnDhXdTZicVFF2Pql430pJ" target="_blank">YouTube videos</a>, and community updates automatically there from my open-source project <a href="https://github.com/jguadagno/jjgnet-broadcast/">JosephGuadagnoNet.Broadcasting</a>. Today, I went to post a message about the noise I was going to be generating on LinkedIn with the API testing and noticed something new.  LinkedIn now has an AI Assisted Editor for Post. The post editor has a slightly new look to it.</p>

<p><img src="/assets/images/posts/2023/linkedin-ai-initial-post.png" alt="LinkedIn AI Assisted Editor" class="align-center" /></p>

<p>So I asked it to help me write a post about the API testing I was doing. I gave it this text:</p>

<blockquote>
  <p>I want to mention to my connections that I will be testing the LinkedIn APIs and they should ignore the post for a while and apologize for the noise it caused</p>
</blockquote>

<p>It came up with the following:</p>

<blockquote>
  <p>Attention all my connections! I will be testing the LinkedIn APIs shortly. Please excuse any noise this may cause and kindly ignore the post for a while. Thank you for your understanding!</p>
</blockquote>

<p>Not bad, the only draw back right now is that it requires a minimum of 30 words to be used. I probably could have written the post with less words but I wanted to try it out.</p>

<p>Once it generated the text, it displayed it in the same dialog that we are used to seeing but with a “thumbs up” and “thumbs down” feedback section.  I clicked the “thumbs up” button since the text was pretty good.</p>

<p><img src="/assets/images/posts/2023/linkedin-ai-generated-post.png" alt="LinkedIn AI Generated Message" class="align-center" /></p>

<p>When I clicked the <strong>Post</strong> button, I received a warning dialog box because I did not edit the text which is a good feature to ensure that you are not posting something that you did not intend to post.</p>

<p><img src="/assets/images/posts/2023/linkedin-ai-warning.png" alt="LinkedIn AI Warning" class="align-center" /></p>

<p>I clicked <strong>Post</strong> since I was fine with the text.</p>

<p>One thing to note, is that, again at the time I am writing this post, it is in <em>Beta</em> and might not be available to everyone.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="AI" /><category term="LinkedIn" /><category term="Generative AI" /><summary type="html"><![CDATA[With me on the hunt for a new job, at the time of this post being authored, I have been pretty active on LinkedIn. The last day or so, I have been experimenting with the LinkedIn API to send my blog posts, YouTube videos, and community updates automatically there from my open-source project JosephGuadagnoNet.Broadcasting. Today, I went to post a message about the noise I was going to be generating on LinkedIn with the API testing and noticed something new. LinkedIn now has an AI Assisted Editor for Post. The post editor has a slightly new look to it.]]></summary></entry><entry><title type="html">Getting Started with Dev Containers</title><link href="https://www.josephguadagno.net/2022/12/10/getting-started-with-developer-containers" rel="alternate" type="text/html" title="Getting Started with Dev Containers" /><published>2022-12-10T15:50:00-07:00</published><updated>2022-12-10T15:50:00-07:00</updated><id>https://www.josephguadagno.net/2022/12/10/getting-started-with-developer-containers</id><content type="html" xml:base="https://www.josephguadagno.net/2022/12/10/getting-started-with-developer-containers"><![CDATA[<p>Recently,
I attended a session at <a href="https://devintersection.com/" target="_blank">DEVIntersections</a> on <a href="https://code.visualstudio.com/docs/remote/containers" target="_blank">Dev Containers</a>
presented by <a href="https://twitter.com/burkeholland" target="_blank">Burke Holland</a>.
Prior to attending the session, I knew of Development Containers, or Dev Containers for short, but didn’t understand them.
This session helped me understand what they are and how to use them.
I figured that I would share what I learned with you.</p>

<h2 id="what-are-dev-containers">What are Dev Containers?</h2>

<p>According to the <a href="https://containers.dev/" target="_blank">Development Containers</a> website, developer containers are:</p>

<p class="notice--info">A Development Container (or Dev Container for short) allows you to use a container as a full-featured development environment. It can be used to run an application, to separate tools, libraries, or run times needed for working with a codebase, and to aid in continuous integration and testing. Development Containers can be run locally or remotely, in a private or public cloud.</p>

<p>Ok, so basically,
a development container uses Docker behind the scenes to create your development environment, so you don’t have to.
Because I do a lot of presentations and demos using different languages and frameworks, it will allow me to have a cleaner machine,
plus be able to work on multiple projects at the same time without having to worry about if I have the correct version of a language or framework installed.</p>

<h2 id="getting-started">Getting Started</h2>

<p>You need three things to get started with development containers:</p>

<ul>
  <li><a href="https://code.visualstudio.com/?wt.mc_id=DT-MVP-4024623" target="_blank">Visual Studio Code</a></li>
  <li><a href="https://www.docker.com/" target="_blank">Docker</a> or another container engine</li>
  <li><a href="vscode:extension/ms-vscode-remote.remote-containers">Dev Containers</a> extension for Visual Studio Code</li>
</ul>

<p>More on getting started can be found <a href="https://code.visualstudio.com/docs/devcontainers/containers?wt.mc_id=DT-MVP-4024623#_getting-started" target="_blank">here</a>.</p>

<h2 id="creating-a-developer-container">Creating a Developer Container</h2>

<p>The Developer Container is described by a file <code class="language-plaintext highlighter-rouge">devcontainer.json</code> which sits in a folder called <code class="language-plaintext highlighter-rouge">.devcontainer</code> in the root of your project.
You can create one in Visual Studio Code
by using the command <code class="language-plaintext highlighter-rouge">Dev Containers: Add Dev Container Configuration Files...</code> from the Command Palette
(<code class="language-plaintext highlighter-rouge">Ctrl+Shift+P</code>).</p>

<p class="notice--info"><strong><em>NOTE</em></strong>: Previous versions of this extension were called <strong>Remote Containers</strong>.</p>

<p>You will be presented with a list of options to choose from:</p>

<table>
  <thead>
    <tr>
      <th>Option</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>From a predefined container configuration definition…</td>
      <td>Use a base configuration from the container definition registry</td>
    </tr>
    <tr>
      <td>From ‘dockerfile’</td>
      <td>Refer to the existing ‘dockerfile’ in the container configuration</td>
    </tr>
    <tr>
      <td>From ‘docker-compose’</td>
      <td>Refer to the existing ‘docker-compose.yml’ in the container configuration</td>
    </tr>
    <tr>
      <td>Learn More</td>
      <td>Documentation on predefined container definitions</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/devcontainer-add-config-file.png" alt="Dev Containers - Add Dev Configuration Files " class="align-center" /></p>

<p>I chose the <code class="language-plaintext highlighter-rouge">From a predefined container configuration definition...</code> option and was presented with a list of options to choose from:</p>

<p><img src="/assets/images/posts/devcontainer-add-config-template.png" alt="Dev Containers - Add Dev Configuration Files - Templates " class="align-center" /></p>

<p>There are a lot of templates to chose from,
since I was working on my blog which is a Jekyll site,
I selected the <code class="language-plaintext highlighter-rouge">*Show All Definitions...*</code> option and chose <code class="language-plaintext highlighter-rouge">Jekyll</code> from the list.</p>

<p>You can see the full list of templates <a href="https://containers.dev/templates" target="_blank">here</a>.</p>

<p>Depending on which container template you choose, you may be presented with additional options or versions.
After the version is selected you will then be prompted to <em>Select additional features to install</em>.
Here is where you can add additional tools to your container like Git, Angular, or Node.js.</p>

<p>A full list of features can be found <a href="https://containers.dev/features" target="_blank">here</a>.</p>

<p>In my scenario, I did not need any additional features, so I clicked <em>Ok</em>.
In a few seconds, the files were created, and I was ready to start working.
There was a <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file created in the <code class="language-plaintext highlighter-rouge">.devcontainer</code> folder in the root of the project.</p>

<p>In my case, there was an extra file called <code class="language-plaintext highlighter-rouge">post-create.sh</code>, more on that later. The <code class="language-plaintext highlighter-rouge">.devcontainer</code> folder now looks like this:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="rouge-code"><pre><span class="err">//</span><span class="w"> </span><span class="err">For</span><span class="w"> </span><span class="err">format</span><span class="w"> </span><span class="err">details,</span><span class="w"> </span><span class="err">see</span><span class="w"> </span><span class="err">https://aka.ms/devcontainer.json.</span><span class="w"> </span><span class="err">For</span><span class="w"> </span><span class="err">config</span><span class="w"> </span><span class="err">options,</span><span class="w"> </span><span class="err">see</span><span class="w"> </span><span class="err">the</span><span class="w">
</span><span class="err">//</span><span class="w"> </span><span class="err">README</span><span class="w"> </span><span class="err">at:</span><span class="w"> </span><span class="err">https://github.com/devcontainers/templates/tree/main/src/jekyll</span><span class="w">
</span><span class="p">{</span><span class="w">
 </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Jekyll"</span><span class="p">,</span><span class="w">
 </span><span class="nl">"image"</span><span class="p">:</span><span class="w"> </span><span class="s2">"mcr.microsoft.com/devcontainers/jekyll:0-buster"</span><span class="p">,</span><span class="w">

 </span><span class="err">//</span><span class="w"> </span><span class="err">Features</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">add</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">dev</span><span class="w"> </span><span class="err">container.</span><span class="w"> </span><span class="err">More</span><span class="w"> </span><span class="err">info:</span><span class="w"> </span><span class="err">https://containers.dev/features.</span><span class="w">
 </span><span class="err">//</span><span class="w"> </span><span class="nl">"features"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w">

 </span><span class="err">//</span><span class="w"> </span><span class="err">Use</span><span class="w"> </span><span class="err">'forwardPorts'</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">make</span><span class="w"> </span><span class="err">a</span><span class="w"> </span><span class="err">list</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">ports</span><span class="w"> </span><span class="err">inside</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">container</span><span class="w"> </span><span class="err">available</span><span class="w"> </span><span class="err">locally.</span><span class="w">
 </span><span class="nl">"forwardPorts"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
  </span><span class="err">//</span><span class="w"> </span><span class="err">Jekyll</span><span class="w"> </span><span class="err">server</span><span class="w">
  </span><span class="mi">4000</span><span class="p">,</span><span class="w">
  </span><span class="err">//</span><span class="w"> </span><span class="err">Live</span><span class="w"> </span><span class="err">reload</span><span class="w"> </span><span class="err">server</span><span class="w">
  </span><span class="mi">35729</span><span class="w">
 </span><span class="p">],</span><span class="w">
 
 </span><span class="err">//</span><span class="w"> </span><span class="err">Use</span><span class="w"> </span><span class="err">'postCreateCommand'</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">run</span><span class="w"> </span><span class="err">commands</span><span class="w"> </span><span class="err">after</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">container</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">created.</span><span class="w">
 </span><span class="nl">"postCreateCommand"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sh .devcontainer/post-create.sh"</span><span class="w">

 </span><span class="err">//</span><span class="w"> </span><span class="err">Configure</span><span class="w"> </span><span class="err">tool-specific</span><span class="w"> </span><span class="err">properties.</span><span class="w">
 </span><span class="err">//</span><span class="w"> </span><span class="nl">"customizations"</span><span class="p">:</span><span class="w"> </span><span class="p">{},</span><span class="w">

 </span><span class="err">//</span><span class="w"> </span><span class="err">Uncomment</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">connect</span><span class="w"> </span><span class="err">as</span><span class="w"> </span><span class="err">root</span><span class="w"> </span><span class="err">instead.</span><span class="w"> </span><span class="err">More</span><span class="w"> </span><span class="err">info:</span><span class="w"> </span><span class="err">https://aka.ms/dev-containers-non-root.</span><span class="w">
 </span><span class="err">//</span><span class="w"> </span><span class="nl">"remoteUser"</span><span class="p">:</span><span class="w"> </span><span class="s2">"root"</span><span class="w">
</span><span class="p">}</span><span class="w">

</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>In the first few lines, outside the comments, you will see a <code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">image</code> property.
The <code class="language-plaintext highlighter-rouge">name</code> is the name of the template and the <code class="language-plaintext highlighter-rouge">image</code> is the image that will be used to create the container.
So, in this example, I am using the <code class="language-plaintext highlighter-rouge">Jekyll</code> template and the <code class="language-plaintext highlighter-rouge">mcr.microsoft.com/devcontainers/jekyll:0-buster</code> image.
Most of the other lines are specific to the type of template you chose, but are relatively common across all templates.</p>

<table>
  <thead>
    <tr>
      <th>Section</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">features</code></td>
      <td>This is where you can add additional tools to your container. <a href="https://containers.dev/features" target="_blank">More details</a></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">forwardPorts</code></td>
      <td>This is where you can forward ports from the container to your local machine.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">postCreateCommand</code></td>
      <td>This is where you can run a script after the container is created.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">customizations</code></td>
      <td>This is where you can customize the instance of Visual Studio Code that will be used in the container.</td>
    </tr>
  </tbody>
</table>

<p>For more details on the <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file, see the <a href="https://containers.dev/implementors/json_reference/?wt.mc_id=DT-MVP-4024623" target="_blank">specification</a>.</p>

<h3 id="jekyll-specific-details">Jekyll Specific Details</h3>

<p>For Jekyll, we need to customize a few things.
First off, we need to forward ports <code class="language-plaintext highlighter-rouge">4000</code> (Jekyll server) and <code class="language-plaintext highlighter-rouge">35729</code> (Live reload server).
This can be done by adding the following to the <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"forwardPorts"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="mi">4000</span><span class="p">,</span><span class="w">
    </span><span class="mi">35729</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>…which is outlined in lines 11-16 in the first <code class="language-plaintext highlighter-rouge">.devcontainers.json</code>.</p>

<p>Next, we need to make sure that <code class="language-plaintext highlighter-rouge">bundler</code> is installed and all the gems are installed.
This is done with the <code class="language-plaintext highlighter-rouge">postCreateCommand</code> property.
For this property, we execute the <code class="language-plaintext highlighter-rouge">post-create.sh</code> script that was created in the <code class="language-plaintext highlighter-rouge">.devcontainer</code> folder.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"postCreateCommand"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sh .devcontainer/post-create.sh"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>The template provides a default <code class="language-plaintext highlighter-rouge">post-create.sh</code> script that looks like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="c">#!/bin/sh</span>

<span class="c"># Install the version of Bundler.</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> Gemfile.lock <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">grep</span> <span class="s2">"BUNDLED WITH"</span> Gemfile.lock <span class="o">&gt;</span> /dev/null<span class="p">;</span> <span class="k">then
    </span><span class="nb">cat </span>Gemfile.lock | <span class="nb">tail</span> <span class="nt">-n</span> 2 | <span class="nb">grep</span> <span class="nt">-C2</span> <span class="s2">"BUNDLED WITH"</span> | <span class="nb">tail</span> <span class="nt">-n</span> 1 | xargs gem <span class="nb">install </span>bundler <span class="nt">-v</span>
<span class="k">fi</span>

<span class="c"># If there's a Gemfile, then run `bundle install`</span>
<span class="c"># It's assumed that the Gemfile will install Jekyll too</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> Gemfile <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span>bundle <span class="nb">install
</span><span class="k">fi</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="customizing-the-visual-studio-code-instance">Customizing the Visual Studio Code Instance</h2>

<p>The <code class="language-plaintext highlighter-rouge">customizations</code> property in the <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file is where
you can customize the instance of Visual Studio Code that will be used in the container.
This is where you can add extensions, settings, and more.
For me, there are four extensions that I need to have when working with Markdown and Jekyll:</p>

<ul>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one" target="_blank">Markdown All in One</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint" target="_blank">Markdown Lint</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=bierner.markdown-emoji" target="_blank">Markdown Emoji</a></li>
  <li><a href="https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker" target="_blank">Code Spell Checker</a></li>
</ul>

<p>You can add them to the <code class="language-plaintext highlighter-rouge">devcontainer.json</code> file like this:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"customizations"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"vscode"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"extensions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="s2">"yzhang.markdown-all-in-one"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"DavidAnson.vscode-markdownlint"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"bierner.markdown-emoji"</span><span class="p">,</span><span class="w">
        </span><span class="s2">"streetsidesoftware.code-spell-checker"</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>You can also add additional settings to the <code class="language-plaintext highlighter-rouge">customizations</code> section.
Details on Visual Studio Code settings can be found <a href="https://containers.dev/supporting#visual-studio-code" target="_blank">here</a>.</p>

<h2 id="using-the-developer-container">Using the Developer Container</h2>

<p>After the <code class="language-plaintext highlighter-rouge">.devcontainer.json</code> file is created you will need to build the container.
You can do this by opening the Command Palette (<code class="language-plaintext highlighter-rouge">Ctrl+Shift+P</code>) and selecting the <strong>Dev Containers: Rebuild and Reopen in Container</strong> command.
This will build the container and open a new instance of Visual Studio Code in the container.
This will take a few minutes the first time you do it, but will be much faster after that.</p>

<p>You may be prompted by a notification that says:</p>

<p class="notice--info">The git repository in the current folder is potentially unsafe as the folder is owned by someone other than the current user.*
If you do get this, click on<em>Manage Unsafe Repositories</em> and then click on repository folder.</p>

<p><img src="/assets/images/posts/devcontainer-unsafe-repository.png" alt="Dev Containers - Unsafe Repository " class="align-center" /></p>

<p>After the container is built and the <code class="language-plaintext highlighter-rouge">postCreateCommand</code> script, if any, is run,
you should see a message in the terminal, “<strong>Done. Press any key to close the terminal.</strong>”.</p>

<p>Feel free to close the terminal and start working on your project.</p>

<h3 id="reopening-the-project">Reopening the Project</h3>

<p>Now with the <code class="language-plaintext highlighter-rouge">.devcontainer.json</code> file in the project, whenever you open the folder in Visual Studio Code,
it will ask you if you want to open the project in a container.</p>

<p><img src="/assets/images/posts/devcontainer-reopen-in-container.png" alt="Dev Containers - Reopen in Container " class="align-center" /></p>

<p><em>Reopen in Container</em>, will open the container and use map the local files to the container.
<em>Clone in Volume</em> will clone the repository into a volume and open the container.
This is generally faster.</p>

<h3 id="closing-the-container">Closing the container</h3>

<p>You can click on the green icon in the lower left corner of the window that says <em>Dev Container:</em>,
along with the name of the container, to close the container.
You can also open the Command Palette (<code class="language-plaintext highlighter-rouge">Ctrl+Shift+P</code>) and select the <strong>Remote: Close Remote Connection</strong> command.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://container.dev/?wt.mc_id=DT-MVP-4024623" target="_blank">Developers Containers</a></li>
  <li><a href="https://code.visualstudio.com/docs/devcontainers/create-dev-container?wt.mc_id=DT-MVP-4024623" target="_blank">Create Dev Container</a></li>
  <li><a href="https://containers.dev/templates?wt.mc_id=DT-MVP-4024623" target="_blank">Dev Container Template</a></li>
  <li><a href="https://containers.dev/features?wt.mc_id=DT-MVP-4024623" target="_blank">Dev Container Features</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Docker" /><category term="Jekyll" /><category term="Containers" /><summary type="html"><![CDATA[Recently, I attended a session at DEVIntersections on Dev Containers presented by Burke Holland. Prior to attending the session, I knew of Development Containers, or Dev Containers for short, but didn’t understand them. This session helped me understand what they are and how to use them. I figured that I would share what I learned with you.]]></summary></entry><entry><title type="html">Visual Studio Code File List Sort Order</title><link href="https://www.josephguadagno.net/2022/12/10/visual-studio-code-file-list-sort-order" rel="alternate" type="text/html" title="Visual Studio Code File List Sort Order" /><published>2022-12-10T08:50:00-07:00</published><updated>2022-12-10T08:50:00-07:00</updated><id>https://www.josephguadagno.net/2022/12/10/visual-studio-code-file-list-sort-order</id><content type="html" xml:base="https://www.josephguadagno.net/2022/12/10/visual-studio-code-file-list-sort-order"><![CDATA[<p>This morning I was working on a blog post on getting started with Developer Containers.
For this, I opened up <a href="https://code.visualstudio.com/?wt.mc_id=DT-MVP-4024623" target="_blank">Visual Studio Code</a>  and <a href="https://www.jetbrains.com/" target="_blank">JetBrains</a> <a href="https://www.jetbrains.com/rider/" target="_blank">Rider</a>.
Once I opened them side by side,
I noticed that the files on the file list in their “Explorers’” were in a different sort order.
This confused me because, at least I think,
they have always been in the same order, <em>dotted</em> (.) folders first,
then <em>underscored</em> (_) folders, then “<em>regular</em>”, then files.
That was not the case for Visual Studio Code it was sorted with <em>underscored</em> (_) folders first,
then <em>dotted</em> (.) folders, then “<em>regular</em>”, then files.</p>

<p><img src="/assets/images/posts/vscode-sort-default.png" alt="Visual Studio Code - Explorer Sort Order - Default " class="align-center" /></p>

<p>Off to Bing, I went to see if I could find a setting to change the sort order.
It turns out that there is a setting to change the sort order.
The setting is <code class="language-plaintext highlighter-rouge">explorer.sortOrderLexicographicOptions</code> and has 4 options:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">default</code> - Default sort order (Uppercase and lowercase names are mixed together)</li>
  <li><code class="language-plaintext highlighter-rouge">lower</code> - Lowercase first (Lowercase names are grouped together before uppercase names)</li>
  <li><code class="language-plaintext highlighter-rouge">upper</code> - Uppercase first (Uppercase names are grouped together before lowercase names)</li>
  <li><code class="language-plaintext highlighter-rouge">unicode</code> - Unicode order (Names are sorted in Unicode order)</li>
</ul>

<p><img src="/assets/images/posts/vscode-sort-options.png" alt="Visual Studio Code - Explorer Sort Order - Options" class="align-center" /></p>

<p>After changing the order to <code class="language-plaintext highlighter-rouge">Unicode</code> the file list was sorted the same way as I had it in JetBrains Rider.</p>

<p><img src="/assets/images/posts/vscode-sort-unicode.png" alt="Visual Studio Code - Explorer Sort Order - Unicode" class="align-center" /></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Visual Studio Code" /><category term="Visual Studio" /><category term="VSCode" /><summary type="html"><![CDATA[This morning I was working on a blog post on getting started with Developer Containers. For this, I opened up Visual Studio Code and JetBrains Rider. Once I opened them side by side, I noticed that the files on the file list in their “Explorers’” were in a different sort order. This confused me because, at least I think, they have always been in the same order, dotted (.) folders first, then underscored (_) folders, then “regular”, then files. That was not the case for Visual Studio Code it was sorted with underscored (_) folders first, then dotted (.) folders, then “regular”, then files.]]></summary></entry><entry><title type="html">Encryption Is Now Required for SQL Server Connection Strings</title><link href="https://www.josephguadagno.net/2022/12/06/encryption-is-now-required-for-sql-server-connection-strings" rel="alternate" type="text/html" title="Encryption Is Now Required for SQL Server Connection Strings" /><published>2022-12-06T04:22:00-07:00</published><updated>2022-12-06T04:22:00-07:00</updated><id>https://www.josephguadagno.net/2022/12/06/encryption-is-now-required-for-sql-server-connection-strings</id><content type="html" xml:base="https://www.josephguadagno.net/2022/12/06/encryption-is-now-required-for-sql-server-connection-strings"><![CDATA[<p>While I was presenting my workshop 
<a href="/presentations/building-and-deploying-a-new-cloud-app-from-scratch" target="_blank">Building and Deploying a New Cloud App from Scratch</a> at <a href="https://www.devintersections.com" target="_blank">DEVIntersections</a> yesterday the connection to the database kept failing. 
Which was weird because I’ve gone through the code quite a few times to make sure everything worked. 
The good thing is
that it failed during the creation of the integration tests which demonstrated the benefit of creating them. 
However, that did not help me on stage.
I was getting the following error:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>A connection was successfully established with the server, but then an error occurred during the login process. 
(provider: SSL Provider, error: 0 - The certificate chain was issued by an authority that is not trusted.)
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I could not figure out what was wrong. 
It worked a few days ago.
Then one attendee said that he saw something similar last week,
and he had to add another parameter to the connection string, <code class="language-plaintext highlighter-rouge">encrypt=optional</code>. 
I added that to the connection string, and it worked.
I was able to continue with the workshop.
However, I was curious why this was happening.
I did some research and found that there was a breaking change to the <code class="language-plaintext highlighter-rouge">Microsoft.Data.SqlClient</code>,
as outlined on the <a href="https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/breaking-changes?wt.mc_id=DT-MVP-4024623#high-impact-changes" target="_blank">Entity Framework Core 7.0 Breaking Changes</a> page.
As it turns out, when I ran this workshop a week or so ago,
I was using <code class="language-plaintext highlighter-rouge">Entity Framework Core</code> version 6.0 along with the corresponding version of <code class="language-plaintext highlighter-rouge">Microsoft.Data.SqlClient</code> version 6.0
and this change was introduced in <code class="language-plaintext highlighter-rouge">Microsoft.Data.SqlClient</code> version 7.0 which <code class="language-plaintext highlighter-rouge">Entity Framework Core</code> version 7.0 uses.</p>

<p>It turns out the change is important.
I/we should have the proper certificates on the SQL Server.
That’s the next thing I need to figure out how to do locally,
so that I can continue to use the default connection string.
However, I’m glad I found the issue and was able to continue with the workshop.</p>

<p>Hopefully, this helps someone else.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET" /><category term="dotnet" /><category term="SQL Server" /><summary type="html"><![CDATA[While I was presenting my workshop Building and Deploying a New Cloud App from Scratch at DEVIntersections yesterday the connection to the database kept failing. Which was weird because I’ve gone through the code quite a few times to make sure everything worked. The good thing is that it failed during the creation of the integration tests which demonstrated the benefit of creating them. However, that did not help me on stage. I was getting the following error:]]></summary></entry><entry><title type="html">Book Review — Software Architecture for Web Developers</title><link href="https://www.josephguadagno.net/2022/10/23/software-architecture-for-web-developers-book-review" rel="alternate" type="text/html" title="Book Review — Software Architecture for Web Developers" /><published>2022-10-23T08:57:00-07:00</published><updated>2022-10-23T08:57:00-07:00</updated><id>https://www.josephguadagno.net/2022/10/23/software-architecture-for-web-developers-book-review</id><content type="html" xml:base="https://www.josephguadagno.net/2022/10/23/software-architecture-for-web-developers-book-review"><![CDATA[<p><img src="/assets/images/posts/book_review-software-architecture-for-web-developers-cover.jpg" alt="Software Architecture for Web Developers Book Cover" class="align-left" /> 
This book is broken up into three parts. 
In the first part, the author defines what software architecture is and what makes a good architecture. 
The second part, the author walks you through the types of Architects and the skill sets needed. 
The author wraps up the book with how a software engineer/developer can become an architect.</p>

<p>Like the definition of Software Architecture, which the author calls out, this book is a bit general. 
However,
it is a good start for someone that does not understand what the role is or does
and either wants to become an architect or work with an architect.</p>

<p>Purchase <a href="https://amzn.to/3N11R4Y" target="_blank">Software Architecture for Web Developers</a> on Amazon.</p>

<p class="notice--info"><strong>Note</strong>: I was given a free copy of this book in return for an honest review.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><category term="dotnet" /><category term="Architecture" /><category term="Web" /><summary type="html"><![CDATA[This book is broken up into three parts. In the first part, the author defines what software architecture is and what makes a good architecture. The second part, the author walks you through the types of Architects and the skill sets needed. The author wraps up the book with how a software engineer/developer can become an architect.]]></summary></entry><entry><title type="html">I’m Running for the Board of the Directors of the DotNet Foundation - 2022 Edition</title><link href="https://www.josephguadagno.net/2022/09/23/im-running-for-the-board-of-directors-of-the-dotnet-foundation-2022-edition" rel="alternate" type="text/html" title="I’m Running for the Board of the Directors of the DotNet Foundation - 2022 Edition" /><published>2022-09-23T04:54:20-07:00</published><updated>2022-09-23T04:54:20-07:00</updated><id>https://www.josephguadagno.net/2022/09/23/im-running-for-the-board-of-directors-of-the-dotnet-foundation-2022-edition</id><content type="html" xml:base="https://www.josephguadagno.net/2022/09/23/im-running-for-the-board-of-directors-of-the-dotnet-foundation-2022-edition"><![CDATA[<p>I am a Microsoft Developer Service MVP, running for a Board of Directors position on the .NET Foundation. I have been an MVP since 2008. I have spoken at multiple conferences throughout the last ten years.</p>

<p class="notice--success">Thank you for electing me to the Board of Directors in 2022</p>

<h2 id="why-im-running">Why I’m Running</h2>

<p>For 20 years or so I have been in Software Development. During that time I have used many tools, languages, and technologies. I started out programming with a small book on QuickBASIC. I later moved on to Visual Basic for DOS. Windows then came along and I starting using Visual Basic for Windows, I then migrated to Visual Basic .NET and eventually ended up using Visual C#. I work as an Senior Engineering Director at Rocket Mortgage, based in Detroit, MI. I am a public speaker and present internationally on a lot of different <a href="https://www.josephguadagno.net/presentations/" target="_blank">technology topics</a>.</p>

<p>One of my primary goals in life is to <em>leave this leave (planet) better than it was when I joined it</em>. I do that in many ways. My favorite way is to grow people and help make them better. I do this through education; blogging, presenting, and public speaking.  I also do this by helping others find ways to help themselves grow.  I believe that being on the board of the .NET Foundation, I can help the vision of .NET and make it easier for others to adopt and grow themselves.  In the past, I have worked with many other talented professionals while serving on the Board of Directors for <a href="#ineta-experience" target="_blank">INETA</a> to help spread the education and adoption of .NET.</p>

<p>For the past 3 years I have been active with the .NET Foundation serving on the <a href="[Membership Committee](https://github.com/dotnet-foundation/wg-membership#readme)" target="blank">Membership Committee</a></p>

<p>I feel I am very well suited for the position.</p>

<p>If you are on Twitter, you can follow the curated list of .NET Foundation Board of Directors 2022 Candidates <a href="https://twitter.com/i/lists/1573087256981737472" target="_blank">here</a>.</p>

<h2 id="my-community-contributions">My Community Contributions</h2>

<h3 id="net-contributions">.NET Contributions</h3>

<ul>
  <li><a href="https://github.com/jguadagno/azure-docs">Azure Documentation</a></li>
  <li><a href="https://github.com/jguadagno/azure-content">Azure Content</a></li>
</ul>

<h3 id="ineta-experience">INETA Experience</h3>

<p>INETA was the International .NET Association.  I served on the board for eight years.  Two as Director of Marketing, four as President, and two as Vice President.</p>

<h3 id="community-contributions">Community Contributions</h3>

<ul>
  <li><a href="https://www.desertcodecamp.com" target="_blank">Organizer of Desert Code Camp</a></li>
  <li><a href="https://www.AZGiveCamp.org" target="_blank">Contributes to AZ Give Camp</a></li>
</ul>

<h3 id="open-source-projects">Open Source Projects</h3>

<ul>
  <li><a href="https://bingsharp.codeplex.com/" target="_blank">BingSharp</a>: A wrapper around the Bing API in C#</li>
  <li><a href="https://github.com/jguadagno/bootstrapThemeSwitcher" target="_blank">bootstrapThemeSwitcher</a>: jquery plugin to dynamically load Bootstrap themes</li>
  <li><a href="https://github.com/jguadagno/twbs-flickrCarousel" target="_blank">twbs-flickrCarousel</a>: a bootstrap carousel to loop through Flickr pictures</li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.Extensions" target="_blank">JosephGuadagno.Extensions</a></li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.Utilities" target="_blank">JosephGuadagno.Utilities</a></li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.Extensions.EF" target="_blank">JosephGuadagno.Extensions.EF</a></li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.Extensions.Web" target="_blank">JosephGuadagno.Extensions.Web</a></li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.AzureHelpers" target="_blank">JosephGuadagno.AzureHelpers</a></li>
</ul>

<h3 id="others">Others</h3>

<ul>
  <li>Public Speaker</li>
  <li>Organized Events at the Microsoft MVP Summit</li>
  <li>Book Reviewer</li>
</ul>

<h2 id="links">Links</h2>

<ul>
  <li><a href="https://www.josephguadagno.net" target="_blank">My blog</a></li>
  <li><a href="https://www.facebook.com/JosephGuadagnoNet/" target="_blank">Facebook Speaking Page</a></li>
</ul>

<h2 id="contact-information">Contact Information</h2>

<ul>
  <li>Twitter: <a href="https://twitter.com/jguadagno" target="_blank">@jguadagno</a></li>
  <li>GitHub: <a href="https://github.com/jguadagno" target="_blank">@jguadagno</a></li>
  <li><a href="https://jjg.me/MVPLink" target="_blank">Microsoft MVP Profile</a></li>
  <li><a href="https://www.linkedin.com/in/josephguadagno/" target="_blank">LinkedIn</a></li>
  <li><a href="https://stackoverflow.com/users/89184/joseph-guadagno" target="_blank">StackOverflow</a></li>
  <li>Email: <a href="mailto:jguadagno@hotmail.com" target="_blank">jguadagno@hotmail.com</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="News" /><category term="Archive" /><category term=".NET" /><category term=".NET Core" /><category term="dotnet" /><category term="Community" /><summary type="html"><![CDATA[I am a Microsoft Developer Service MVP, running for a Board of Directors position on the .NET Foundation. I have been an MVP since 2008. I have spoken at multiple conferences throughout the last ten years.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://avatars3.githubusercontent.com/u/3209610?s=250" /><media:content medium="image" url="https://avatars3.githubusercontent.com/u/3209610?s=250" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Book Review - Parallel Programming and Concurrency with C# 10 and .NET 6</title><link href="https://www.josephguadagno.net/2022/09/19/parallel-programming-and-concurrency-with-csharp-10-and-dotnet6" rel="alternate" type="text/html" title="Book Review - Parallel Programming and Concurrency with C# 10 and .NET 6" /><published>2022-09-19T04:22:00-07:00</published><updated>2022-09-19T04:22:00-07:00</updated><id>https://www.josephguadagno.net/2022/09/19/parallel-programming-and-concurrency-with-csharp-10-and-dotnet6</id><content type="html" xml:base="https://www.josephguadagno.net/2022/09/19/parallel-programming-and-concurrency-with-csharp-10-and-dotnet6"><![CDATA[<p><img src="/assets/images/posts/parallel-programming-book-cover.jpg" alt="Parallel Programming and Concurrency with C# 10 and .NET 6 Book Cover" class="align-left" /> You may have wanted to dabble in Parallel Programing (async/await), the Task Parallel Library (TPL), or other ways to make your application more responsive and have been a little worried about the complexity. This book is for you. It’s a great introduction to the concepts and how to use them in your application. It’s also a great reference for when you need to look up how to do something specific.</p>

<p>This book is for beginner to intermediate-level .NET developers who want to employ the latest parallel and concurrency features in .NET when building their applications. It has a lot of real-world examples that you can use in your own applications. You’ll learn best practices to avoid application dead-locks, how to safely update your UI, debug your parallel code, and more.</p>

<p>This book should be in your library if you’re a .NET developer.</p>

<p>Purchase <a href="https://amzn.to/3f3zlTh" target="_blank">Parallel Programming and Concurrency with C# 10 and .NET 6</a> on Amazon.</p>

<p class="notice--info"><strong>Note</strong>: I was a technical reviewer for this book.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><category term="dotnet" /><category term="csharp" /><category term="Visual Studio" /><category term=".NET" /><category term=".NET Core" /><summary type="html"><![CDATA[You may have wanted to dabble in Parallel Programing (async/await), the Task Parallel Library (TPL), or other ways to make your application more responsive and have been a little worried about the complexity. This book is for you. It’s a great introduction to the concepts and how to use them in your application. It’s also a great reference for when you need to look up how to do something specific.]]></summary></entry><entry><title type="html">2022 Fall Speaking Events</title><link href="https://www.josephguadagno.net/2022/09/07/2022-fall-speaking-events" rel="alternate" type="text/html" title="2022 Fall Speaking Events" /><published>2022-09-07T04:22:00-07:00</published><updated>2022-09-07T04:22:00-07:00</updated><id>https://www.josephguadagno.net/2022/09/07/2022-fall-speaking-events</id><content type="html" xml:base="https://www.josephguadagno.net/2022/09/07/2022-fall-speaking-events"><![CDATA[<p>I’m excited to announce that I’ll be speaking at the following events this fall:</p>

<h2 id="twin-cities-net-user-group">Twin Cities .NET User Group</h2>

<p><img src="/assets/images/posts/2022-fall-speaking-events-twincities-usergroup.jpeg" alt="Twin Cities .NET User Group" class="align-left" /></p>

<dl>
  <dt>When</dt>
  <dd>September 8, 2022</dd>
  <dt>Where</dt>
  <dd>Online</dd>
  <dt>Topic</dt>
  <dd><a href="/presentations/communication-is-hard-make-it-better-with-azure-communication-services">Communication is Hard. Make it better with Azure Communication Services</a></dd>
  <dt>URL</dt>
  <dd><a href="https://www.meetup.com/tcdnug/events/288181972/" target="_blank">https://www.meetup.com/tcdnug/events/288181972/</a></dd>
</dl>

<h2 id="that-conference-online---september">THAT Conference Online - September</h2>

<p><img src="/assets/images/posts/2022-fall-speaking-events-august-22-that-online.svg" alt="THAT Conference Online" class="align-left" /></p>

<dl>
  <dt>When</dt>
  <dd>September 15, 2022</dd>
  <dt>Where</dt>
  <dd>Online</dd>
  <dt>Topics</dt>
  <dd><a href="/presentations/build-and-ship-it">Build and Ship It!</a>, and <a href="/presentations/communication-is-hard-make-it-better-with-azure-communication-services">Communication is Hard. Make it better with Azure Communication Services</a></dd>
  <dt>URL</dt>
  <dd><a href="https://that.us/events/thatus/2022-9/" target="_blank">https://that.us/events/thatus/2022-9/</a></dd>
</dl>

<h2 id="c-corner-conference---india-tour">C# Corner Conference - India Tour</h2>

<p><img src="/assets/images/posts/2022-fall-speaking-events-csharp-corner.jpeg" alt="C# Corner Conference" class="align-left" /></p>

<dl>
  <dt>When</dt>
  <dd>October 8-16, 2022</dd>
  <dt>Where</dt>
  <dd>Chennai, India &amp; Hyderabad, India &amp; Mumbai, India &amp; Jaipur, India &amp; Delhi, India</dd>
  <dt>Topics</dt>
  <dd><a href="/presentations/application-architecture-patterns">Application Architecture Pattern</a>, and <a href="/presentations/scaling-your-dotnet-application-using-azure">Scaling Your .NET Application with Azure</a></dd>
  <dt>URL</dt>
  <dd><a href="https://conference.c-sharpcorner.com" target="_blank">https://conference.c-sharpcorner.com</a></dd>
</dl>

<h2 id="techbash">TechBash</h2>

<p><img src="/assets/images/posts/2022-fall-speaking-events-techbash.jpeg" alt="TechBash" class="align-left" /></p>

<dl>
  <dt>When</dt>
  <dd>November 8-11, 2022</dd>
  <dt>Where</dt>
  <dd>Poconos, Pennsylvania</dd>
  <dt>Topics</dt>
  <dd><a href="/presentations/building-and-deploying-a-new-cloud-app-from-scratch">Building and Deploying a New Cloud App from Scratch</a>, and <a href="/presentations/application-architecture-patterns">Application Architecture Patterns</a></dd>
  <dt>Url</dt>
  <dd><a href="https://www.techbash.com/" target="_blank">https://www.techbash.com/</a></dd>
</dl>

<h2 id="update-conference">Update Conference</h2>

<p><img src="/assets/images/posts/2022-fall-speaking-events-update-conference.jpeg" alt="Update Conference - Prague" class="align-left" /></p>

<dl>
  <dt>When</dt>
  <dd>November 10-11, 2022</dd>
  <dt>Where</dt>
  <dd>Prague, Czech Republic</dd>
  <dt>Topics</dt>
  <dd><a href="/presentations/debugging-tips-and-tricks-with-jetbrains-rider">Debugging Tips and Tricks with JetBrains Rider</a></dd>
  <dt>Url</dt>
  <dd><a href="https://updateconference.net/" target="_blank">https://updateconference.net/</a></dd>
</dl>

<h2 id="european-sharepoint-office-365--azure-conference">European SharePoint Office 365 &amp; Azure Conference</h2>

<p><img src="/assets/images/posts/2022-fall-speaking-events-espc.jpg" alt="European SharePoint Office 365 &amp; Azure Conference" class="align-left" /></p>

<dl>
  <dt>When</dt>
  <dd>November 28, 2022 to December 01, 2022</dd>
  <dt>Where</dt>
  <dd>Copenhagen, Denmark</dd>
  <dt>Topics</dt>
  <dd><a href="/presentations/scaling-your-dotnet-application-using-azure">Scaling Your .NET Application Using Azure</a></dd>
  <dt>Url</dt>
  <dd><a href="https://www.sharepointeurope.com/" target="_blank">https://www.sharepointeurope.com/</a></dd>
</dl>

<h2 id="devintersection">DevIntersection</h2>

<p><img src="/assets/images/posts/2022-fall-speaking-events-devintersection.png" alt="DEVIntersection" class="align-left" /></p>

<dl>
  <dt>When</dt>
  <dd>December 4-9, 2022</dd>
  <dt>Where</dt>
  <dd>Las Vegas, Nevada</dd>
  <dt>Topics</dt>
  <dd><strong>Workshop</strong> <a href="/presentations/building-and-deploying-a-new-cloud-app-from-scratch">Building and Deploying a New Cloud App from Scratch</a>, <a href="/presentations/application-architecture-patterns">Application Architecture Patterns</a>, and <a href="/presentations/communication-is-hard-make-it-better-with-azure-communication-services">Communication is Hard. Make it better with Azure Communication Services</a></dd>
  <dt>Url</dt>
  <dd><a href="https://www.devintersection.com/" target="_blank">https://www.devintersection.com/</a></dd>
  <dt>Comments</dt>
  <dd>Register with code <strong>GUADAGNO50</strong> for $50 off registration</dd>
</dl>]]></content><author><name>Joseph Guadagno</name></author><category term="News" /><category term="Archive" /><category term="Speaking" /><category term="Presentation" /><category term="Presentations" /><category term="presenting" /><summary type="html"><![CDATA[I’m excited to announce that I’ll be speaking at the following events this fall:]]></summary></entry><entry><title type="html">How to simplify your C# unit testing with a mocking framework</title><link href="https://www.josephguadagno.net/2022/07/19/how-to-simplify-your-c-sharp-unit-testing-with-a-mocking-framework" rel="alternate" type="text/html" title="How to simplify your C# unit testing with a mocking framework" /><published>2022-07-19T04:22:00-07:00</published><updated>2022-07-19T04:22:00-07:00</updated><id>https://www.josephguadagno.net/2022/07/19/how-to-simplify-your-c-sharp-unit-testing-with-a-mocking-framework</id><content type="html" xml:base="https://www.josephguadagno.net/2022/07/19/how-to-simplify-your-c-sharp-unit-testing-with-a-mocking-framework"><![CDATA[<p>It’s time to take your unit testing to the next level.  You’ve implemented either <a href="https://www.nunit.org">NUnit</a>, or <a href="https://xunit.net">xUnit</a>, or <a href="https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest?wt.mc_id=DT-MVP-4024623">MSTest</a> in your projects. You’ve gotten your code coverage to 80+%. But the are just some things that are hard to test or validate in your project.  How do you test the “business logic” in your repository class?  How do you test your dependent web service or database? Yeah, you can write special unit test and create fake objects to mimic these dependencies but why waste your time writing code that does not ship with the end product. Or write a lot of code with the <code class="language-plaintext highlighter-rouge">ExcludeFromCoverage</code> attribute :smile:. Well this is mocking comes in.</p>

<p>Mocking is a framework that allows you to create a mock object that can be used to simulate the behavior of a real object.  You can use the mock object to verify that the real object was called with the expected parameters, and to verify that the real object was not called with unexpected parameters.  You can also verify that the real object was called the expected number of times.  You can also verify that the real object was called with the expected parameters and that the real object was not called with unexpected parameters. The possibilities are endless. Mocking comes in three flavors: <em>fakes</em>, <em>stubs</em>, and <em>mocks</em>. The fakes are the simplest.  They are used when you want to test the behavior of a class that has no dependencies.  The stubs are used when you want to test the behavior of a class that has dependencies.  The mocks are used when you want to test the behavior of a class that has dependencies.</p>

<p><em>For more information on mocking and the differences between stubs, fakes and mocks read the <a href="https://www.telerik.com/blogs/30-days-of-tdd-day-11-what-s-the-deal-with-mocking">Fakes, Stubs and Mocks</a> blog post.</em></p>

<h2 id="getting-started-with-mocking">Getting Started with Mocking</h2>

<p>First, you’ll need a mocking framework to get started.  Something like Telerik <a href="https://www.telerik.com/products/mocking.aspx">JustMock</a> or their free version <a href="https://www.telerik.com/justmock/free-mocking">JustMock Lite</a>.</p>

<p>A mocking framework is what you creates the objects and <em>“pretends”</em> to be the object(s) you are testings.</p>

<h3 id="arrange-act-assert">Arrange, Act, Assert</h3>

<p>Now that you have a mocking framework, let’s get started with the primary parts of the unit testing process, Arrange, Act, Assert. Arrange, Act, Assert, or AAA, is a common term used to describe the process of setting up the test environment, executing the test, and verifying the results. It’s a best practice in unit testing. Basically, each of your unit tests should have these three parts:</p>

<ul>
  <li>Arrange: Set up the test.</li>
  <li>Act: Execute the test.</li>
  <li>Assert: Verify the results.</li>
</ul>

<p>When I write tests, in this case using xUnit, I generally start with this “stub” pattern:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="n">Fact</span><span class="p">]</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">GetContact_WithAnInvalidId_ShouldReturnNull</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Arrange </span>

    <span class="c1">// Act</span>

    <span class="c1">// Assert</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The method name is follows a consistent format:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">[Fact]</code>: This is a fact test.</li>
  <li><code class="language-plaintext highlighter-rouge">public void</code>: This is a public method.</li>
  <li><code class="language-plaintext highlighter-rouge">GetContact</code>: This is the method you are testing.</li>
  <li><code class="language-plaintext highlighter-rouge">_WithAnInvalidId_</code>: Whatever variables you are using, in this example, an <em>invalid id</em>.</li>
  <li><code class="language-plaintext highlighter-rouge">ShouldReturnNull</code>: The expected outcome.</li>
</ul>

<p>While this convention is not required, I tend to use it so when I am looking at the results, or another engineer is looking at the code, he/she can see the intent of the test.</p>

<p><img src="/assets/images/posts/mocking-unit-test-success.jpg" alt="All Unit Tests Pass Successfully" /></p>

<h2 id="mocking-stuff">Mocking Stuff</h2>

<p>There are a lot of different types of things to mock, like services, databases, queues, and other types of dependencies. For this introductory example, I am going to demonstrate different ways to test a class that requires a dependency of a database. I’ll be using <a href="https://xunit.net">xUnit</a> and Telerik <a href="[https](https://www.telerik.com/products/mocking.aspx)">JustMock</a> for these examples.</p>

<p>The project used in this example can be found <a href="https://github.com/jguadagno/contacts-mocking">here</a>. This project is a C# project that was build on my <a href="https://www.twitch.tv/jguadagno">Twitch</a> stream. The application provides a way to manage a list of contacts. It uses a variety of technologies including:</p>

<ul>
  <li>Web API</li>
  <li>SQL Server</li>
  <li>ASP.NET Core</li>
  <li>ASP.NET Core MVC</li>
  <li>Azure Storage</li>
  <li>Azure Functions</li>
</ul>

<p>With all these dependencies I needed a way to validate that these dependencies are working as expected. And before you ask, no, I am not testing the functionality of SQL Server, or Azure Storage, or Azure Functions.  I am only testing the interaction with this services. That’s where mocking comes in. For the rest of this post, I’ll focus on testing the <code class="language-plaintext highlighter-rouge">ContactManager</code> class and mocking the <code class="language-plaintext highlighter-rouge">ContactRepository</code>.</p>

<p>Before we get started, let’s take a look at the <code class="language-plaintext highlighter-rouge">ContactManager</code> class. The <code class="language-plaintext highlighter-rouge">ContactManager</code> implements the <code class="language-plaintext highlighter-rouge">IContactManager</code> interface. This is what we are testing.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">interface</span> <span class="nc">IContactManager</span>
<span class="p">{</span>
    <span class="n">Contact</span> <span class="nf">GetContact</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">);</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;</span> <span class="nf">GetContacts</span><span class="p">();</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;</span> <span class="nf">GetContacts</span><span class="p">(</span><span class="kt">string</span> <span class="n">firstName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lastName</span><span class="p">);</span>
    <span class="n">Contact</span> <span class="nf">SaveContact</span><span class="p">(</span><span class="n">Contact</span> <span class="n">contact</span><span class="p">);</span>
    <span class="kt">bool</span> <span class="nf">DeleteContact</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">);</span>
    <span class="kt">bool</span> <span class="nf">DeleteContact</span><span class="p">(</span><span class="n">Contact</span> <span class="n">contact</span><span class="p">);</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="n">Phone</span><span class="p">&gt;</span> <span class="nf">GetContactPhones</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">);</span>
    <span class="n">Phone</span> <span class="nf">GetContactPhone</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">,</span> <span class="kt">int</span> <span class="n">phoneId</span><span class="p">);</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="n">Address</span><span class="p">&gt;</span> <span class="nf">GetContactAddresses</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">);</span>
    <span class="n">Address</span> <span class="nf">GetContactAddress</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">,</span> <span class="kt">int</span> <span class="n">addressId</span><span class="p">);</span>
    <span class="c1">/// Other methods removed for brevity</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Full code for this class can be found <a href="https://github.com/jguadagno/contacts-mocking/blob/main/src/Contacts.Domain/Interfaces/IContactManager.cs">here</a></p>

<p>We’ll be mocking the <code class="language-plaintext highlighter-rouge">ContactRepository</code> object which implements the <code class="language-plaintext highlighter-rouge">IContactRepository</code> interface.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">interface</span> <span class="nc">IContactRepository</span>
<span class="p">{</span>
    <span class="n">Contact</span> <span class="nf">GetContact</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">);</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;</span> <span class="nf">GetContacts</span><span class="p">();</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;</span> <span class="nf">GetContacts</span><span class="p">(</span><span class="kt">string</span> <span class="n">firstName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">lastName</span><span class="p">);</span>
    <span class="n">Contact</span> <span class="nf">SaveContact</span><span class="p">(</span><span class="n">Contact</span> <span class="n">contact</span><span class="p">);</span>
    <span class="kt">bool</span> <span class="nf">DeleteContact</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">);</span>
    <span class="kt">bool</span> <span class="nf">DeleteContact</span><span class="p">(</span><span class="n">Contact</span> <span class="n">contact</span><span class="p">);</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="n">Phone</span><span class="p">&gt;</span> <span class="nf">GetContactPhones</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">);</span>
    <span class="n">Phone</span> <span class="nf">GetContactPhone</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">,</span> <span class="kt">int</span> <span class="n">phoneId</span><span class="p">);</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="n">Address</span><span class="p">&gt;</span> <span class="nf">GetContactAddresses</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">);</span>
    <span class="n">Address</span> <span class="nf">GetContactAddress</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">,</span> <span class="kt">int</span> <span class="n">addressId</span><span class="p">);</span>
    <span class="c1">/// Other methods removed for brevity</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>While objects that are being mocked don’t need to be interfaces, it certainly helps.  The <code class="language-plaintext highlighter-rouge">IContactManager</code> interface is a contract that defines the methods that interact with a <em>contact</em>.  The <code class="language-plaintext highlighter-rouge">ContactManager</code> class implements the <code class="language-plaintext highlighter-rouge">IContactManager</code> interface, in this case.  However, one thing to note is that the <code class="language-plaintext highlighter-rouge">ContactManager</code> requires an <code class="language-plaintext highlighter-rouge">IContactRepository</code> dependency, which is what we are going to mock. The <code class="language-plaintext highlighter-rouge">IContactRepository</code> interface, defines the contract with the database, which we do not want to test.  This is were mocking comes in.  We want to be able to test that the logic in the <code class="language-plaintext highlighter-rouge">ContactManager</code> class is working as expected without going back and forth with the database. This allows use to test things like validation or objects on save, returning the correct exceptions when things go wrong, etc.</p>

<h3 id="our-first-mock">Our First Mock</h3>

<p>Let’s start with the most common test. Let’s validate that a call to <code class="language-plaintext highlighter-rouge">GetContacts</code> returns a list of contacts.  We’ll start with the simplest test, and then move to more complex tests.</p>

<p>The signature of <code class="language-plaintext highlighter-rouge">GetContacts</code> is:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Task</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;&gt;</span> <span class="nf">GetContacts</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>If we start with our template from above, we should stub out a test that looks like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">void</span> <span class="nf">GetContacts_ShouldReturnLists</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Arrange</span>

    <span class="c1">// Act</span>

    <span class="c1">// Assert</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="arrange-the-test">Arrange: The test</h4>

<p>Now, let’s look at the <em>arrange</em> part. For the arrange part, we need to setup the mocks so that the mocking framework knows what to mimic or mock. Here’s the arrange part for the <code class="language-plaintext highlighter-rouge">GetContacts_ShouldReturnLists</code> method:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">mockContactRepository</span> <span class="p">=</span> <span class="n">Mock</span><span class="p">.</span><span class="n">Create</span><span class="p">&lt;</span><span class="n">IContactRepository</span><span class="p">&gt;();</span>
<span class="n">Mock</span><span class="p">.</span><span class="nf">Arrange</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">mockContactRepository</span><span class="p">.</span><span class="nf">GetContacts</span><span class="p">())</span>
  <span class="p">.</span><span class="nf">Returns</span><span class="p">(</span><span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;</span>
  <span class="p">{</span>
      <span class="k">new</span> <span class="n">Contact</span> <span class="p">{</span> <span class="n">ContactId</span> <span class="p">=</span> <span class="m">1</span> <span class="p">},</span> <span class="k">new</span> <span class="n">Contact</span> <span class="p">{</span> <span class="n">ContactId</span> <span class="p">=</span> <span class="m">2</span> <span class="p">}</span>
  <span class="p">});</span>
<span class="kt">var</span> <span class="n">contactManager</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ContactManager</span><span class="p">(</span><span class="n">mockContactRepository</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>On line 1, we create a variable, <code class="language-plaintext highlighter-rouge">mockContactRepository</code> that is the mock of the <code class="language-plaintext highlighter-rouge">IContactRepository</code> interface.  Line 2, we create a mock of the <code class="language-plaintext highlighter-rouge">GetContacts</code> method.  Lines 3-6, we create a list of contacts and tell the the mock framework return this object when a call is made to <code class="language-plaintext highlighter-rouge">GetObjects</code>. Finally, on line 7, we create a new <code class="language-plaintext highlighter-rouge">ContactManager</code> object and pass in the mock <code class="language-plaintext highlighter-rouge">IContactRepository</code> object.</p>

<h4 id="act-execute-on-the-test">Act: Execute on the test</h4>

<p>In this case, the <em>act</em> is trivial.  We just call the <code class="language-plaintext highlighter-rouge">GetContacts</code> method on the <code class="language-plaintext highlighter-rouge">ContactManager</code> object.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">contacts</span> <span class="p">=</span> <span class="n">contactManager</span><span class="p">.</span><span class="nf">GetContacts</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This should return a list of contacts with two contacts with the ids of 1 and 2.</p>

<h4 id="assert-verify-the-results">Assert: verify the results</h4>

<p>Let’s validate that the list of contacts has two contacts.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">Assert</span><span class="p">.</span><span class="nf">NotNull</span><span class="p">(</span><span class="n">contacts</span><span class="p">);</span>
<span class="n">Assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">contacts</span><span class="p">.</span><span class="n">Count</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Line 1 is checking that the list of contacts is not null.  Line 2 is checking that the list of contacts has two contacts.</p>

<h4 id="the-complete-test">The Complete Test</h4>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="n">Fact</span><span class="p">]</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">GetContacts_ShouldReturnLists</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Arrange</span>
    <span class="kt">var</span> <span class="n">mockContactRepository</span> <span class="p">=</span> <span class="n">Mock</span><span class="p">.</span><span class="n">Create</span><span class="p">&lt;</span><span class="n">IContactRepository</span><span class="p">&gt;();</span>
    <span class="n">Mock</span><span class="p">.</span><span class="nf">Arrange</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">mockContactRepository</span><span class="p">.</span><span class="nf">GetContacts</span><span class="p">())</span>
      <span class="p">.</span><span class="nf">Returns</span><span class="p">(</span><span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;</span>
      <span class="p">{</span>
        <span class="k">new</span> <span class="n">Contact</span> <span class="p">{</span> <span class="n">ContactId</span> <span class="p">=</span> <span class="m">1</span> <span class="p">},</span> <span class="k">new</span> <span class="n">Contact</span> <span class="p">{</span> <span class="n">ContactId</span> <span class="p">=</span> <span class="m">2</span> <span class="p">}</span>
      <span class="p">});</span>

    <span class="kt">var</span> <span class="n">contactManager</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ContactManager</span><span class="p">(</span><span class="n">mockContactRepository</span><span class="p">);</span>

    <span class="c1">// Act</span>
    <span class="kt">var</span> <span class="n">contacts</span> <span class="p">=</span> <span class="n">contactManager</span><span class="p">.</span><span class="nf">GetContacts</span><span class="p">();</span>

    <span class="c1">// Assert</span>
    <span class="n">Assert</span><span class="p">.</span><span class="nf">NotNull</span><span class="p">(</span><span class="n">contacts</span><span class="p">);</span>
    <span class="n">Assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="m">2</span><span class="p">,</span> <span class="n">contacts</span><span class="p">.</span><span class="n">Count</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="mocking-with-ranges">Mocking with Ranges</h3>

<p>There is a method in the <code class="language-plaintext highlighter-rouge">ContactManager</code> called <code class="language-plaintext highlighter-rouge">GetContact</code> which requires an integer as a parameter. In our business case, the identifier of a contact is a positive number (integer). So let’s set up some test that make sure a call to get <code class="language-plaintext highlighter-rouge">GetContact</code> with a negative number returns <code class="language-plaintext highlighter-rouge">null</code> and a call to get <code class="language-plaintext highlighter-rouge">GetContact</code> with a positive number returns a contact.</p>

<p>For this, we’ll use a feature called <em>matchers</em>. Matchers let you ignore passing actual values as arguments used in mocks. Instead, they give you the possibility to pass just an expression that satisfies the argument type or the expected value range. The means that we don’t have to write a test for each possible value. We can just write a test for the range of values. We are going to use the <code class="language-plaintext highlighter-rouge">InRange</code> matcher for our two tests.</p>

<p>For the test, <code class="language-plaintext highlighter-rouge">GetContact_WithAnInvalidId_ShouldReturnNull</code> where we expect a <code class="language-plaintext highlighter-rouge">null</code> return, we would arrange the test like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">Mock</span><span class="p">.</span><span class="nf">Arrange</span><span class="p">(()</span> <span class="p">=&gt;</span> 
  <span class="n">mockContactRepository</span><span class="p">.</span><span class="nf">GetContact</span><span class="p">(</span><span class="n">Arg</span><span class="p">.</span><span class="nf">IsInRange</span><span class="p">(</span><span class="kt">int</span><span class="p">.</span><span class="n">MinValue</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">RangeKind</span><span class="p">.</span><span class="n">Inclusive</span><span class="p">)))</span>
    <span class="p">.</span><span class="n">Returns</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;(</span><span class="k">null</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In this arrangement, we are saying that when a call to <code class="language-plaintext highlighter-rouge">GetContact</code> is made with an argument that is in the range of <code class="language-plaintext highlighter-rouge">int.MinValue</code> to 0, inclusive, we should return <code class="language-plaintext highlighter-rouge">null</code>.</p>

<p>Our act and assert looks like:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="c1">// Act</span>
<span class="kt">var</span> <span class="n">contact</span> <span class="p">=</span> <span class="n">contactManager</span><span class="p">.</span><span class="nf">GetContact</span><span class="p">(-</span><span class="m">1</span><span class="p">);</span> <span class="c1">// Any number less than zero</span>

<span class="c1">// Assert</span>
<span class="n">Assert</span><span class="p">.</span><span class="nf">Null</span><span class="p">(</span><span class="n">contact</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>For the test, <code class="language-plaintext highlighter-rouge">GetContact_WithAnValidId_ShouldReturnContact</code>, we would arrange the test like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="n">Mock</span><span class="p">.</span><span class="nf">Arrange</span><span class="p">(()</span> <span class="p">=&gt;</span>
  <span class="n">mockContactRepository</span><span class="p">.</span><span class="nf">GetContact</span><span class="p">(</span><span class="n">Arg</span><span class="p">.</span><span class="nf">IsInRange</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="kt">int</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">,</span> <span class="n">RangeKind</span><span class="p">.</span><span class="n">Inclusive</span><span class="p">)))</span>
    <span class="p">.</span><span class="nf">Returns</span><span class="p">(</span>
    <span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="n">Contact</span>
    <span class="p">{</span>
        <span class="n">ContactId</span> <span class="p">=</span> <span class="n">contactId</span>
    <span class="p">});</span>

<span class="kt">var</span> <span class="n">contactManager</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ContactManager</span><span class="p">(</span><span class="n">mockContactRepository</span><span class="p">);</span>
<span class="k">const</span> <span class="kt">int</span> <span class="n">requestedContactId</span> <span class="p">=</span> <span class="m">1</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This one required a little bit more work because we needed to specific an object to return, lines 3 to 6, and a value for the contact id, line 9, to validate in our test.</p>

<p>Our act and assert looks like:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="c1">// Act</span>
<span class="c1">// Assumes that a contact record exists with the ContactId of 1</span>
<span class="kt">var</span> <span class="n">contact</span> <span class="p">=</span> <span class="n">contactManager</span><span class="p">.</span><span class="nf">GetContact</span><span class="p">(</span><span class="n">requestedContactId</span><span class="p">);</span>

<span class="c1">// Assert</span>
<span class="n">Assert</span><span class="p">.</span><span class="nf">NotNull</span><span class="p">(</span><span class="n">contact</span><span class="p">);</span>
<span class="n">Assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="n">requestedContactId</span><span class="p">,</span> <span class="n">contact</span><span class="p">.</span><span class="n">ContactId</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="mocking-with-exceptions">Mocking with Exceptions</h3>

<p>The <code class="language-plaintext highlighter-rouge">GetContacts</code> method has an overload which expects two string parameters, one for first name and the other for last name.  The method, also requires that the first name and last name are not <code class="language-plaintext highlighter-rouge">null</code> or empty. If so, it should throw an <code class="language-plaintext highlighter-rouge">ArgumentNullException</code>. Let’s create a test that validates that a call to <code class="language-plaintext highlighter-rouge">GetContacts</code> with an empty first name and last name throws the exception.</p>

<p>Let’s arrange the test like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="c1">// Arrange </span>
<span class="kt">var</span> <span class="n">mockContactRepository</span> <span class="p">=</span> <span class="n">Mock</span><span class="p">.</span><span class="n">Create</span><span class="p">&lt;</span><span class="n">IContactRepository</span><span class="p">&gt;();</span>
<span class="n">Mock</span><span class="p">.</span><span class="nf">Arrange</span><span class="p">(()</span> <span class="p">=&gt;</span>
    <span class="n">mockContactRepository</span><span class="p">.</span><span class="nf">GetContacts</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="n">Arg</span><span class="p">.</span><span class="n">IsAny</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;()));</span>

<span class="kt">var</span> <span class="n">contactManager</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ContactManager</span><span class="p">(</span><span class="n">mockContactRepository</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here we are passing a <code class="language-plaintext highlighter-rouge">null</code> for the <code class="language-plaintext highlighter-rouge">FirstName</code> parameter and using the <code class="language-plaintext highlighter-rouge">Arg.IsAny&lt;string&gt;</code> matcher for the <code class="language-plaintext highlighter-rouge">LastName</code> parameter which will match any string.</p>

<p>Our act, which is also and asset, looks like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="c1">// Act</span>
<span class="n">ArgumentNullException</span> <span class="n">ex</span> <span class="p">=</span>
  <span class="n">Assert</span><span class="p">.</span><span class="n">Throws</span><span class="p">&lt;</span><span class="n">ArgumentNullException</span><span class="p">&gt;(()</span> <span class="p">=&gt;</span> <span class="n">contactManager</span><span class="p">.</span><span class="nf">GetContacts</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="s">"Guadagno"</span><span class="p">));</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here we are creating a variable <code class="language-plaintext highlighter-rouge">ex</code> which is of type <code class="language-plaintext highlighter-rouge">ArgumentNullException</code> and then we are asserting that the <code class="language-plaintext highlighter-rouge">GetContacts</code> method throws an <code class="language-plaintext highlighter-rouge">ArgumentNullException</code> when called with the <code class="language-plaintext highlighter-rouge">FirstName</code> parameter set to <code class="language-plaintext highlighter-rouge">null</code> and the <code class="language-plaintext highlighter-rouge">LastName</code> parameter set to <code class="language-plaintext highlighter-rouge">Guadagno</code>.</p>

<p>Then in the assert, we are checking that the exception message is correct.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="c1">// Assert</span>
<span class="n">Assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="s">"firstName"</span><span class="p">,</span> <span class="n">ex</span><span class="p">.</span><span class="n">ParamName</span><span class="p">);</span>
<span class="n">Assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="s">"FirstName is a required field (Parameter 'firstName')"</span><span class="p">,</span> <span class="n">ex</span><span class="p">.</span><span class="n">Message</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Note, JustMock supports an alternative way of asserting that an exception is thrown. We can use <code class="language-plaintext highlighter-rouge">Mock.Arrange</code> to assert that an exception is thrown.  We can use the <code class="language-plaintext highlighter-rouge">Throws</code> matcher to assert that an exception is thrown.  We can use the <code class="language-plaintext highlighter-rouge">Throws&lt;T&gt;</code> matcher to assert that an exception of a specific type is thrown.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">Mock</span><span class="p">.</span><span class="nf">Arrange</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="n">contactManager</span><span class="p">.</span><span class="nf">GetContacts</span><span class="p">(</span><span class="k">null</span><span class="p">,</span> <span class="s">"Guadagno"</span><span class="p">))</span>
  <span class="p">.</span><span class="n">Throws</span><span class="p">&lt;</span><span class="n">ArgumentNullException</span><span class="p">&gt;(</span><span class="s">"FirstName is a required field (Parameter 'firstName')"</span><span class="p">);</span> 
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="complete-contact-tests">Complete Contact Tests</h3>

<p>The complete code for the <code class="language-plaintext highlighter-rouge">ContactManager</code> class can be found <a href="https://github.com/jguadagno/contacts-mocking/blob/main/src/Contacts.Logic/ContactManager.cs">here</a>.
The complete code for the <code class="language-plaintext highlighter-rouge">ContactManagerTest</code> class can be found <a href="https://github.com/jguadagno/contacts-mocking/blob/main/src/Contacts.Logic.Tests/ContactManagerTests.cs">here</a>.</p>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>This just scratches the surface of mocking.  There are many more ways to mock using a mocking framework like JustMock.  Maybe we’ll cover more in a future post.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Mock" /><category term="Unit Tests" /><category term="testing" /><summary type="html"><![CDATA[It’s time to take your unit testing to the next level. You’ve implemented either NUnit, or xUnit, or MSTest in your projects. You’ve gotten your code coverage to 80+%. But the are just some things that are hard to test or validate in your project. How do you test the “business logic” in your repository class? How do you test your dependent web service or database? Yeah, you can write special unit test and create fake objects to mimic these dependencies but why waste your time writing code that does not ship with the end product. Or write a lot of code with the ExcludeFromCoverage attribute :smile:. Well this is mocking comes in.]]></summary></entry><entry><title type="html">My JetBrains Rider Plugins—June 2022 Edition</title><link href="https://www.josephguadagno.net/2022/06/23/my-jetbrains-rider-plugins-june-2022-edition" rel="alternate" type="text/html" title="My JetBrains Rider Plugins—June 2022 Edition" /><published>2022-06-23T17:25:00-07:00</published><updated>2024-07-12T21:27:00-07:00</updated><id>https://www.josephguadagno.net/2022/06/23/my-jetbrains-rider-plugins-june-2022-edition</id><content type="html" xml:base="https://www.josephguadagno.net/2022/06/23/my-jetbrains-rider-plugins-june-2022-edition"><![CDATA[<p class="notice--warning">I’ve updated my plugins list for JetBrains Rider.  You can find the updated list in the post <a href="/2025/08/23/my-jetbrains-rider-plugins-august-2025-edition" target="_blank">My JetBrains Rider Plugins — August 2025 Edition</a>.</p>

<p>I’ve been using <a href="https://www.jetbrains.com/" target="_blank">JetBrains</a> <a href="https://www.jetbrains.com/rider/" target="_blank">Rider</a> for quite some time now.</p>

<p>I started using JetBrains Rider because I used both a Windows PC and a Mac PC and I wanted a consistent experience between the both.
JetBrains Rider provided that and now I am hooked.</p>

<p>I thought I’d take some time to blog about the plugins I use to help with my day-to-day coding (mostly in Microsoft .NET and Azure), presenting (public speaking), and blogging (using <a href="https://jekyllrb.com/" target="_blank">Jekyll</a>).</p>

<p>All of these plugins can be downloaded from either the JetBrains plugin <a href="https://plugins.jetbrains.com/" target="_blank">Marketplace</a> or directly in the IDE.</p>

<p>So here is the list.</p>

<h2 id="general-development">General Development</h2>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/9525--env-files-support" target="_blank">.env files support</a></td>
      <td>As the name implies, it provides environment variable completion for <code class="language-plaintext highlighter-rouge">Dockerfile</code> and <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> files</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/17718-github-copilot" target="_blank">GitHub Copilot</a></td>
      <td>GitHub Copilot uses OpenAI Codex to suggest code and entire functions in real-time right from your editor. <strong><em>Note</em></strong>: This plugin is free but requires a paid subscription service</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/9792-key-promoter-x" target="_blank">Key Promoter X</a></td>
      <td>Let’s you know if there is a keystroke shortcut for any mouse based IDE commands</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/10249-powershell" target="_blank">PowerShell</a></td>
      <td>Provides PowerShell intellisense and script execution support for IntelliJ IDEs</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/10080-rainbow-brackets" target="_blank">Rainbow Brackets</a></td>
      <td>Provides colored brackets, parentheses, and lines in the IDE</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/2162-string-manipulation" target="_blank">String Manipulation</a></td>
      <td>Case switching, sorting, filtering, incrementing, aligning to columns, grepping, escaping, encoding… Very helpful when working with tabular data</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/12832-structured-logging" target="_blank">Structured Logging</a></td>
      <td>Contains some useful analyzers for structured logging. Supports Serilog, NLog, and Microsoft.Extensions.Logging</td>
    </tr>
  </tbody>
</table>

<h3 id="microsoft-specific-development">Microsoft Specific Development</h3>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/10183--net-core-user-secrets" target="_blank">.NET Core User Secrets</a></td>
      <td>Provides the ability to create and edit user secrets in .NET projects</td>
    </tr>
  </tbody>
</table>

<h3 id="azure-specific-development">Azure Specific Development</h3>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/13984-application-insights-debug-log-viewer" target="_blank">Application Insights Debug Log Viewer</a></td>
      <td>Provides the ability to view Azure Monitor (Application Insights) telemetry in the IDE</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/7981-azure-devops" target="_blank">Azure DevOps</a></td>
      <td>Azure DevOps is a plugin to enable working with Git and TFVC repositories on Azure DevOps Services or Team Foundation Server (TFS) 2015+</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/11220-azure-toolkit-for-rider" target="_blank">Azure Toolkit for Rider</a></td>
      <td>Rider plugin for integration with Azure cloud services. Allow to create, configure, and deploy .Net Core and .Net Web Apps to Azure from Rider on all supported platforms</td>
    </tr>
  </tbody>
</table>

<h2 id="presenting">Presenting</h2>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/7345-presentation-assistant" target="_blank">Presentation Assistant</a></td>
      <td>This plugin shows name and Win/Mac shortcuts of any action you invoke</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/18045-window-resizer" target="_blank">Window Resizer</a></td>
      <td>This plugin let you quickly resize windows to any of the following predefined orientations/sizes</td>
    </tr>
  </tbody>
</table>

<h2 id="writing">Writing</h2>

<table>
  <thead>
    <tr>
      <th>Plugin</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/12175-grazie" target="_blank">Grazie</a></td>
      <td>Intelligent spelling and grammar checks for any text you write in the IDE</td>
    </tr>
    <tr>
      <td><a href="https://plugins.jetbrains.com/plugin/16136-grazie-professional" target="_blank">Grazie Professional</a></td>
      <td><strong>Enhances the base Grazie plugin with advanced writing assistance for English text in your IDE</strong>. Grazie Professional is a result of the latest developments in deep learning and natural language processing</td>
    </tr>
  </tbody>
</table>

<h2 id="wrap-up">Wrap Up</h2>

<p>Like I started out with, these are the plugins I use every day for coding, presenting, and writing.</p>

<p>Do you have a favorite not listed?</p>

<p>Let me know.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term="JetBrains" /><category term="Rider" /><category term="Plugin" /><category term="presenting" /><summary type="html"><![CDATA[I’ve updated my plugins list for JetBrains Rider. You can find the updated list in the post My JetBrains Rider Plugins — August 2025 Edition.]]></summary></entry><entry><title type="html">Clean up some .NET Clutter</title><link href="https://www.josephguadagno.net/2022/06/15/clean-up-some-dot-net-clutter" rel="alternate" type="text/html" title="Clean up some .NET Clutter" /><published>2022-06-15T14:55:00-07:00</published><updated>2022-06-15T14:55:00-07:00</updated><id>https://www.josephguadagno.net/2022/06/15/clean-up-some-dot-net-clutter</id><content type="html" xml:base="https://www.josephguadagno.net/2022/06/15/clean-up-some-dot-net-clutter"><![CDATA[<p>As some of you know, I do a lot of public speaking and blogging, 
although the blogging seems to have taken a vacation lately :smile:.
One of the side effects of blogging and speaking is that I have a lot of versions of .NET on my machine, along with other software.
Today, after installing the latest version of <a href="https://devblogs.microsoft.com/dotnet/announcing-dotnet-7-preview-5/" target="_blank">.NET</a>, I took a look at what versions of the SDK and runtime I have on my machine.
I was a bit surprised to see that as to how many versions of .NET were on my machine.</p>

<p>I had around 20 versions of .NET SDKs on my machine.</p>

<p><img src="/assets/images/posts/dot-net-cleanup-sdk-before.png" alt=".NET SDKs Before Cleanup" /></p>

<p>And close to 20 versions of .NET Runtime on my machine.</p>

<p><img src="/assets/images/posts/dot-net-cleanup-runtime-before.png" alt=".NET Runtimes Before Cleanup" /></p>

<p>I decided to clean up some older versions of .NET SDKs and Runtime.
This is where the <a href="https://docs.microsoft.com/en-us/dotnet/core/additional-tools/uninstall-tool?tabs=windowsapplication&amp;?wt.mc_id=DT-MVP-4024623">.NET Uninstall Tool</a> comes in handy. This tool allows you to see older versions of .NET SDKs and Runtime and uninstall them.</p>

<p>You can download the tool <a href="https://github.com/dotnet/cli-lab/releases" target="_blank">here</a>.
There are instructions for installation for both Windows and macOS.</p>

<h2 id="using-the-uninstall-tool">Using the Uninstall Tool</h2>

<h3 id="see-the-versions-of-net-sdks-and-runtime">See the Versions of .NET SDKs and Runtime</h3>

<p>Once the tool is installed,
you can use it to see the versions of .NET SDKs and Runtime that are installed on your machine by executing the following command:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="o">.</span><span class="n">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">list</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>This will show you a list similar to the images above.</p>

<p>If you are not ready to uninstall a particular version of the .NET SDK or Runtime, you can use the following command to see what would happen if you were to uninstall that version:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="o">.</span><span class="n">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">dry-run</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="uninstall-the-net-sdks-and-runtime">Uninstall the .NET SDKs and Runtime</h3>

<p>There are quite a few different ways to uninstall the .NET SDKs and Runtime’s.
For each option you need to choose either the <code class="language-plaintext highlighter-rouge">sdk</code>, <code class="language-plaintext highlighter-rouge">runtime</code>, <code class="language-plaintext highlighter-rouge">aspnet-runtime</code>, or <code class="language-plaintext highlighter-rouge">hosting-bundle</code>.
You then need to specify which versions you want to uninstall.
There are options for <code class="language-plaintext highlighter-rouge">all</code>, <code class="language-plaintext highlighter-rouge">all-but-latest</code>, <code class="language-plaintext highlighter-rouge">latest</code>, and many more.
You can see the full list of options <a href="https://docs.microsoft.com/en-us/dotnet/core/additional-tools/uninstall-tool?tabs=windows&amp;application?wt.mc_id=DT-MVP-4024623#options-1" target="_blank">here</a>.</p>

<p>I chose to use the <code class="language-plaintext highlighter-rouge">all-previews-but-latest</code> option, this will uninstall all the previews .NET SDKs and Runtime versions except the latest version of the specific preview.
And the <code class="language-plaintext highlighter-rouge">all-but-latest</code> option, which will uninstall all the .NET SDKs and Runtime versions except the latest version of the major version.</p>

<p>After running each of the commands, my machine will look like the image below.</p>

<p><img src="/assets/images/posts/dot-net-cleanup-after.png" alt=".NET SDKs and Runtime After Cleanup" /></p>

<h3 id="script-to-uninstall-net-sdks-and-runtime">Script to Uninstall .NET SDKs and Runtime</h3>

<p>Here is the script I used to uninstall the .NET SDKs and Runtime.</p>

<p class="notice--info">Note: the script requires elevated permissions to run.</p>

<h4 id="macos">macOS</h4>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="n">sudo</span><span class="w"> </span><span class="o">.</span><span class="nx">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">remove</span><span class="w"> </span><span class="nt">--all-previews-but-latest</span><span class="w"> </span><span class="nt">--sdk</span><span class="w">
</span><span class="n">sudo</span><span class="w"> </span><span class="o">.</span><span class="nx">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">remove</span><span class="w"> </span><span class="nt">--all-previews-but-latest</span><span class="w"> </span><span class="nt">--runtime</span><span class="w">
</span><span class="n">sudo</span><span class="w"> </span><span class="o">.</span><span class="nx">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">remove</span><span class="w"> </span><span class="nt">--all-lower-patches</span><span class="w"> </span><span class="nt">--sdk</span><span class="w">
</span><span class="n">sudo</span><span class="w"> </span><span class="o">.</span><span class="nx">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">remove</span><span class="w"> </span><span class="nt">--all-lower-patches</span><span class="w"> </span><span class="nt">--runtime</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="windows">Windows</h4>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="o">.</span><span class="n">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">remove</span><span class="w"> </span><span class="nt">--all-previews-but-latest</span><span class="w"> </span><span class="nt">--sdk</span><span class="w">
</span><span class="o">.</span><span class="n">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">remove</span><span class="w"> </span><span class="nt">--all-previews-but-latest</span><span class="w"> </span><span class="nt">--runtime</span><span class="w">
</span><span class="o">.</span><span class="n">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">remove</span><span class="w"> </span><span class="nt">--all-lower-patches</span><span class="w"> </span><span class="nt">--sdk</span><span class="w">
</span><span class="o">.</span><span class="n">/dotnet-core-uninstall</span><span class="w"> </span><span class="nx">remove</span><span class="w"> </span><span class="nt">--all-lower-patches</span><span class="w"> </span><span class="nt">--runtime</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="wrap-up">Wrap Up</h2>

<p>It was pretty easy to unclutter my machine of the older versions of .NET SDKs and Runtime
once I had the Uninstall Tool installed and ran the script.
Now, I have to remember to run this script every once in a while.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="dotnet" /><category term="sdk" /><category term="runtime" /><category term="csharp" /><category term="vb" /><category term="visualbasic" /><category term=".NET Core" /><category term=".NET" /><summary type="html"><![CDATA[As some of you know, I do a lot of public speaking and blogging, although the blogging seems to have taken a vacation lately :smile:. One of the side effects of blogging and speaking is that I have a lot of versions of .NET on my machine, along with other software. Today, after installing the latest version of .NET, I took a look at what versions of the SDK and runtime I have on my machine. I was a bit surprised to see that as to how many versions of .NET were on my machine.]]></summary></entry><entry><title type="html">Enabling user authentication in Swagger using Microsoft Identity</title><link href="https://www.josephguadagno.net/2022/06/03/enabling-user-authentication-in-swagger-using-microsoft-identity" rel="alternate" type="text/html" title="Enabling user authentication in Swagger using Microsoft Identity" /><published>2022-06-03T14:00:00-07:00</published><updated>2022-06-03T14:00:00-07:00</updated><id>https://www.josephguadagno.net/2022/06/03/enabling-user-authentication-in-swagger-using-microsoft-identity</id><content type="html" xml:base="https://www.josephguadagno.net/2022/06/03/enabling-user-authentication-in-swagger-using-microsoft-identity"><![CDATA[<p>I’ve been working on a personal project of mine called “<a href="https://github.com/jguadagno/jjgnet-broadcast" target="_blank">JosephGuadagno.NET Broadcasting</a>”. I know a terrible name, but I’ll work on a better name when it’s ready to be launched. :smile: This application is a web application that allows users to broadcast their content to their followers. One of the components of the application is an API that allows users to create and schedule their content in the application. The application is built using ASP.NET Core, Azure, Swagger, and other <a href="https://github.com/jguadagno/jjgnet-broadcast/blob/main/infrastructure-needs.md" target="_blank">components</a>. I recently secured the application using Microsoft’s <a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/msal-overview?wt.mc_id=DT-MVP-4024623" target="_blank">MSAL</a> library. Now, I’m going to show you how to enable user authentication in Swagger using Microsoft Identity.</p>

<p class="notice--info"><strong><em>Note</em></strong>: When I started creating this post, Microsoft is/was in the process of renaming Microsoft Identity to <a href="https://www.microsoft.com/en-us/security/business/microsoft-entra" target="_blank">Microsoft Entra</a>. As a result, some of the links in this post might change in the future. :slightly_frowning_face:</p>

<h2 id="assumptions">Assumptions</h2>

<p>I’m going to assume that you already have an API secured with Microsoft Identity, if not, you can check out a series I put together previously called <a href="/2020/06/12/protecting-an-asp-net-core-api-with-microsoft-identity-platform">Protecting an ASP.NET Core API with Microsoft Identity Platform</a>.</p>

<p>I’m also going to assume that you already have swagger configured. If not, check out <a href="https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-6.0&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">this</a></p>

<h2 id="configure-swagger-to-use-microsoft-identity">Configure Swagger to use Microsoft Identity</h2>

<p>First step is to add the security requirements, <code class="language-plaintext highlighter-rouge">AddSecurityRequirement</code>, and security definitions, <code class="language-plaintext highlighter-rouge">AddSecurityDefinition</code> to the Swagger configuration.</p>

<p>Locate in your application code, typically in the <code class="language-plaintext highlighter-rouge">Program.cs</code>, the <code class="language-plaintext highlighter-rouge">AddSwaggerGen</code> method and add the following code:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
</pre></td><td class="rouge-code"><pre><span class="c1">// Enabled OAuth security in Swagger</span>
<span class="kt">var</span> <span class="n">scopes</span> <span class="p">=</span> <span class="n">JosephGuadagno</span><span class="p">.</span><span class="n">Broadcasting</span><span class="p">.</span><span class="n">Domain</span><span class="p">.</span><span class="n">Scopes</span><span class="p">.</span><span class="nf">ToDictionary</span><span class="p">(</span><span class="n">settings</span><span class="p">.</span><span class="n">ApiScopeUrl</span><span class="p">);</span>
<span class="n">scopes</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="s">$"</span><span class="p">{</span><span class="n">settings</span><span class="p">.</span><span class="n">ApiScopeUrl</span><span class="p">}</span><span class="s">user_impersonation"</span><span class="p">,</span> <span class="s">"Access application on user behalf"</span><span class="p">);</span>
<span class="n">c</span><span class="p">.</span><span class="nf">AddSecurityRequirement</span><span class="p">(</span><span class="k">new</span> <span class="nf">OpenApiSecurityRequirement</span><span class="p">()</span> <span class="p">{</span>  
    <span class="p">{</span>  
        <span class="k">new</span> <span class="n">OpenApiSecurityScheme</span> <span class="p">{</span>  
            <span class="n">Reference</span> <span class="p">=</span> <span class="k">new</span> <span class="n">OpenApiReference</span> <span class="p">{</span>  
                <span class="n">Type</span> <span class="p">=</span> <span class="n">ReferenceType</span><span class="p">.</span><span class="n">SecurityScheme</span><span class="p">,</span>  
                <span class="n">Id</span> <span class="p">=</span> <span class="s">"oauth2"</span>  
            <span class="p">},</span>  
            <span class="n">Scheme</span> <span class="p">=</span> <span class="s">"oauth2"</span><span class="p">,</span>  
            <span class="n">Name</span> <span class="p">=</span> <span class="s">"oauth2"</span><span class="p">,</span>  
            <span class="n">In</span> <span class="p">=</span> <span class="n">ParameterLocation</span><span class="p">.</span><span class="n">Header</span>  
        <span class="p">},</span>  
        <span class="k">new</span> <span class="n">List</span> <span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="p">()</span>  
    <span class="p">}</span>  
<span class="p">});</span>   
<span class="n">c</span><span class="p">.</span><span class="nf">AddSecurityDefinition</span><span class="p">(</span><span class="s">"oauth2"</span><span class="p">,</span> <span class="k">new</span> <span class="n">OpenApiSecurityScheme</span>
<span class="p">{</span>
    <span class="n">Type</span> <span class="p">=</span> <span class="n">SecuritySchemeType</span><span class="p">.</span><span class="n">OAuth2</span><span class="p">,</span>
    <span class="n">Flows</span> <span class="p">=</span> <span class="k">new</span> <span class="n">OpenApiOAuthFlows</span>
    <span class="p">{</span>
        <span class="n">Implicit</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">OpenApiOAuthFlow</span><span class="p">()</span>
        <span class="p">{</span>
            <span class="n">AuthorizationUrl</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"https://login.microsoftonline.com/common/oauth2/v2.0/authorize"</span><span class="p">),</span>
            <span class="n">TokenUrl</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"https://login.microsoftonline.com/common/common/v2.0/token"</span><span class="p">),</span>
            <span class="n">Scopes</span> <span class="p">=</span> <span class="n">scopes</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You can also view the code <a href="https://github.com/jguadagno/jjgnet-broadcast/blob/d9648a049b172cd600dcfb8a5847ff6e852ddbc9/src/JosephGuadagno.Broadcasting.Api/Program.cs#L62-L92" target="_blank">here</a>.</p>

<p>Most of this code does not need to change except the <code class="language-plaintext highlighter-rouge">scopes</code> variable (lines 1 and 2) and the <code class="language-plaintext highlighter-rouge">AuthorizationUrl</code> and <code class="language-plaintext highlighter-rouge">TokenUrl</code> (lines 25 and 26).</p>

<p>The <code class="language-plaintext highlighter-rouge">scopes</code> variable is a dictionary that maps the scope name to the scope description. The scope description is used in the swagger UI to describe the scope.  This should be a list of scopes that you want your Swagger UI to have access to.</p>

<p class="notice--info"><strong><em>BTW</em></strong>, this should only been done in a development and/or testing environment.  In most cases, you will not want to enable the Swagger UI in production.</p>

<p>The <code class="language-plaintext highlighter-rouge">AuthorizationUrl</code> and <code class="language-plaintext highlighter-rouge">TokenUrl</code> may change depending on the tenant and/or organization type you selected when you created your application.  You can find the correct values on the <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#register-an-application?wt.mc_id=DT-MVP-4024623" target="_blank">Register an Application</a> page.</p>

<h2 id="get-a-client-secret-for-swagger-ui">Get a Client Secret for Swagger UI</h2>

<p>First, we’ll need to get client secret for the Swagger UI so that the application can authenticate users to the API. We will need to go to the application in the whichever Azure Active Directory it is registered in.  In this example, I have an application named “<em>JosephGuadagno.NET Broadcasting (Test) - API</em>” registered in my <em>Default Directory</em>.</p>

<p>Now let’s see how to get the client secret.</p>

<ul>
  <li>Navigate to the <a href="https://portal.azure.com">Azure Portal</a></li>
  <li>Navigate to the respective directory. <em>Default Directory</em> in this example.</li>
  <li>Click on <em>App registrations</em></li>
  <li>Click the application for the API</li>
</ul>

<p>You should see something similar to the following:</p>

<p><img src="/assets/images/posts/swagger-msal-registered-app.png" alt="Registered App" /></p>

<p>Next, click on the <em>Certificates &amp; secrets</em> menu item. After that, click on the <em>Client Secrets</em> tab item, then <em>+ New client secret</em>. All highlighted in red in the image below.</p>

<p><img src="/assets/images/posts/swagger-msal-create-client-secret.png" alt="Create client secret" /></p>

<p>In the dialog box that follows, enter the following:</p>

<table>
  <tbody>
    <tr>
      <td>Field</td>
      <td>Description</td>
      <td>Value</td>
    </tr>
    <tr>
      <td>Description</td>
      <td>A description of the client</td>
      <td><code class="language-plaintext highlighter-rouge">SwaggerClient</code></td>
    </tr>
    <tr>
      <td>Expires</td>
      <td>How long the client secret will be valid for</td>
      <td><code class="language-plaintext highlighter-rouge">6 months</code></td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Click <em>Add</em></li>
</ul>

<p>After it is done creating the client secret, you should see something similar to the following:</p>

<p><img src="/assets/images/posts/swagger-msal-create-client-secret-created.png" alt="Created client secret" /></p>

<p class="notice--info"><strong><em>NOTE</em></strong>: Copy the client secret and store it securely. You’ll need it for the next step and once this blade closes, you cannot access the client secret again.</p>

<h2 id="enable-swagger-ui-to-use-microsoft-identity">Enable Swagger UI to use Microsoft Identity</h2>

<p>Locate in your code the <code class="language-plaintext highlighter-rouge">UseSwaggerUI</code> method and add the following code:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">app</span><span class="p">.</span><span class="nf">UseSwaggerUI</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">OAuthAppName</span><span class="p">(</span><span class="s">"Swagger Client"</span><span class="p">);</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">OAuthClientId</span><span class="p">(</span><span class="s">"&lt;Your client id&gt;"</span><span class="p">);</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">OAuthClientSecret</span><span class="p">(</span><span class="s">"&lt;Your client secret&gt;"</span><span class="p">);</span>
    <span class="n">options</span><span class="p">.</span><span class="nf">OAuthUseBasicAuthenticationWithAccessCodeGrant</span><span class="p">();</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now, you’ll need to replace the <code class="language-plaintext highlighter-rouge">&lt;Your client id&gt;</code> with the client id of the registered application. In the example above it is labeled as <em>Application (client) ID</em> and starts with <em>027edf</em>.  The <code class="language-plaintext highlighter-rouge">&lt;Your client secret&gt;</code> is the client secret you copied earlier.</p>

<h2 id="testing-out-the-swagger-ui">Testing out the Swagger UI</h2>

<p>If everything is configured correctly, once you start your API and navigate to the Swagger UI, you should see something similar to the following:</p>

<p><img src="/assets/images/posts/swagger-msal-secured-swagger-ui.png" alt="Secured Swagger UI" /></p>

<p>Clicking on the <em>Authorize</em> button will bring up a page showing you some information about the application, including the name, authorization url, client_id, and scopes being requested.</p>

<p><img src="/assets/images/posts/swagger-msal-available-authorizations.png" alt="Secured Swagger UI" /></p>

<ul>
  <li>Select all the permissions that you want to test</li>
  <li>Click the <code class="language-plaintext highlighter-rouge">Authorize</code> button to start the sign in process.</li>
</ul>

<p>The Microsoft Identity will redirect you to the authorization url.  Once you are redirected, you will be able to sign in with your Microsoft account. And you will be presented with a page that shows you the permissions that you have selected and allows you to authorize this application to access those permissions.</p>

<p><img src="/assets/images/posts/swagger-msal-microsoft-authorize-ui.png" alt="Microsoft Authorize UI" /></p>

<p>The Swagger UI will let you know that it received the authorization.</p>

<p><img src="/assets/images/posts/swagger-msal-swagger-authorized-response.png" alt="Swagger Authorized Response" /></p>

<p>Now the Swagger UI will look like this:</p>

<p><img src="/assets/images/posts/swagger-msal-swagger-is-authorized.png" alt="Swagger is Authorized" /></p>

<h2 id="wrap-up">Wrap Up</h2>

<p>So with a little work, we can see how easy it is to use the Swagger UI to authenticate users to the API. This is a great way to test out the API and make sure that it is working as expected.</p>

<p>Most of the code here can be just copied and pasted into future projects.  The only thing you would need to do is tweak the scopes and update the client id/secret.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://swagger.io/docs/specification/authentication/oauth2">Swagger OAuth2 documentation</a></li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-registering-an-application">Microsoft Identity - Register an application</a></li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-configure-local-development">Microsoft Identity - Configure Local Development</a></li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-assigning-a-role">Microsoft Identity - Assigning a Role</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="swagger" /><category term="Identity" /><category term="MSAL" /><category term="Managed Identity" /><category term="Entra" /><summary type="html"><![CDATA[I’ve been working on a personal project of mine called “JosephGuadagno.NET Broadcasting”. I know a terrible name, but I’ll work on a better name when it’s ready to be launched. :smile: This application is a web application that allows users to broadcast their content to their followers. One of the components of the application is an API that allows users to create and schedule their content in the application. The application is built using ASP.NET Core, Azure, Swagger, and other components. I recently secured the application using Microsoft’s MSAL library. Now, I’m going to show you how to enable user authentication in Swagger using Microsoft Identity.]]></summary></entry><entry><title type="html">This post was written with GitHub Copilot</title><link href="https://www.josephguadagno.net/2022/01/13/this-post-was-written-with-github-copilot" rel="alternate" type="text/html" title="This post was written with GitHub Copilot" /><published>2022-01-13T18:15:00-07:00</published><updated>2022-01-13T18:15:00-07:00</updated><id>https://www.josephguadagno.net/2022/01/13/this-post-was-written-with-github-copilot</id><content type="html" xml:base="https://www.josephguadagno.net/2022/01/13/this-post-was-written-with-github-copilot"><![CDATA[<p>In this post, I will be writing about how I used GitHub Copilot to write this post. You might be thinking that GitHub Copilot is a for writing code and you would right. However, it is a tool for writing content also.</p>

<p>This post is based on the following products:</p>

<table>
  <thead>
    <tr>
      <th>Product</th>
      <th>Version</th>
      <th>Download Link</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Visual Studio Code</td>
      <td>1.67.0</td>
      <td><a href="https://code.visualstudio.com/Download" target="_blank">Download</a></td>
    </tr>
    <tr>
      <td>GitHub Copilot</td>
      <td>1.7.4421</td>
      <td><a href="https://copilot.github.com/" target="_blank">Sign up</a></td>
    </tr>
    <tr>
      <td>GitHub Copilot Extension</td>
      <td>1.7.4421</td>
      <td><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot" target="_blank">Install</a></td>
    </tr>
  </tbody>
</table>

<p class="notice--info">Note: Github Copilot is in Technical Preview when this post was written and is subject to change.</p>

<p>Now some of the suggestions that GitHub Copilot makes are a little <em>freaky</em> as to how good the predictions are. I’ll cover some of them in this post.</p>

<p>Before I show you some off them, I wanted to show you that GitHub pilot started working on the post before I even write the first word. When I start writing a post, I first start out with file name which is typically formatted <code class="language-plaintext highlighter-rouge">yyyy-mm-dd-title-of-post.md</code>, which by the way this was suggested by GitHub Copilot. :smile: The file name for this post is <code class="language-plaintext highlighter-rouge">this-post-was-written-with-github-copilot.md</code>. After the file is created I start working on the header or metadata for the post. Once I started typing <code class="language-plaintext highlighter-rouge">title: :</code> for the header or metadata of this post, GitHub Copilot suggested the title of the post.</p>

<p><img src="/assets/images/posts/github-copilot-title.png" alt="Title Suggestion" /></p>

<p>This is what a typical header looks like:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="nn">---</span>
<span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">This</span><span class="nv"> </span><span class="s">post</span><span class="nv"> </span><span class="s">was</span><span class="nv"> </span><span class="s">written</span><span class="nv"> </span><span class="s">with</span><span class="nv"> </span><span class="s">GitHub</span><span class="nv"> </span><span class="s">Copilot"</span>
<span class="na">header</span><span class="pi">:</span>
    <span class="na">og_image</span><span class="pi">:</span> <span class="s">/assets/images/posts/header/github-copilot-writing.png</span>
<span class="na">date</span><span class="pi">:</span> <span class="s">2022-01-13 17:30:00 -0700</span>
<span class="na">categories</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">Articles</span>
<span class="na">tags</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">GitHub</span>
  <span class="pi">-</span> <span class="s">CoPilot</span>
  <span class="pi">-</span> <span class="s">blog</span>
<span class="nn">---</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I recorded a <a href="https://www.youtube.com/watch?v=-xK215uA6QI" target="_blank">video</a> while I was writing this post so you can see, if you wish, how I use GitHub CoPilot to write this posts. If you watch the video, you will see my typing errors but more so how GitHub Copilot suggests the text for this post.</p>

<p>Now let’s look at some of the suggestions that GitHub Copilot has made.</p>

<h2 id="tip-1-lists-in-markdown">Tip 1: Lists in Markdown</h2>

<p>This blog is written using Markdown, more on this in a future post. In a previous post, I created a list of items I wanted to cover.  Sample list below:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="gu">## Presentation</span>

C# 10 language features
ASP.NET Changes
Maui, no, not the beach
Performance improvements
New APIs
Other enhancements
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Once I added the list above and start typing <code class="language-plaintext highlighter-rouge">###</code> to create a new heading Github Copilot is going to suggest the next item in the list.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="gu">### C# 10 language features</span>
<span class="gu">### ASP.NET Changes</span>
<span class="gu">### Maui, no, not the beach</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><img src="/assets/images/posts/github-copilot-lists.gif" alt="Clipboard" /></p>

<p>Minute marker: 3:21 to 6:38</p>

<h2 id="tip-2-clipboard">Tip 2: Clipboard</h2>

<p>Github Copilot is going to suggest items from the clipboard that make sense. I am going to copy the url to this blog <code class="language-plaintext highlighter-rouge">josephguadagno.net</code> to the clipboard, then create a link in the document.</p>

<p>Link to the blog: <a href="https://josephguadagno.net">josephguadagno.net</a></p>

<p>That’s pretty cool.</p>

<p><img src="/assets/images/posts/github-copilot-clipboard.gif" alt="Clipboard" /></p>

<p>Minute Marker: 8:28 to 8:40</p>

<h2 id="tip-3-markdown-headers">Tip 3: Markdown Headers</h2>

<p>After I typed the <code class="language-plaintext highlighter-rouge">##</code> to indicate a H2 heading, Github copilot suggested that I name it <code class="language-plaintext highlighter-rouge">Tip 3: Markdown Preview</code> but if I have labeled my headers as <code class="language-plaintext highlighter-rouge">Tip 1</code>, <code class="language-plaintext highlighter-rouge">Tip 2</code>, etc is I had in the draft of this post all I would have had to do is hit <code class="language-plaintext highlighter-rouge">Tab</code> and <code class="language-plaintext highlighter-rouge">Enter</code>.</p>

<p><img src="/assets/images/posts/github-copilot-headers.gif" alt="Headers" /></p>

<p>Minute Marker: 8:55 to 9:07</p>

<h2 id="tip-4-patterns">Tip 4: Patterns</h2>

<p>Github Copilot is going to suggest patterns that make sense. I am going to create a pattern in the document.</p>

<p><code class="language-plaintext highlighter-rouge">DistinctBy/InsertBy/RemoveBy/UpdateBy/WhereBy</code></p>

<p>After I typed <code class="language-plaintext highlighter-rouge">DistinctBy/InsertBy/</code>, after I type <code class="language-plaintext highlighter-rouge">Remove</code> the <code class="language-plaintext highlighter-rouge">By/</code> was added. Github Copilot recognized the <code class="language-plaintext highlighter-rouge">*By/</code> pattern and suggested the next item in the list.</p>

<p><img src="/assets/images/posts/github-copilot-headers.gif" alt="Headers" /></p>

<p>Minute Marker: 12:10 to 12:35</p>

<h2 id="things-to-note">Things to Note</h2>

<p>Validate URLs, it makes assumptions based on previous URLs or patterns that might not match the actual URL.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>As I discover more about the features of GitHub Copilot, I will add to this post.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="GitHub" /><category term="CoPilot" /><category term="Copilot" /><category term="blog" /><summary type="html"><![CDATA[In this post, I will be writing about how I used GitHub Copilot to write this post. You might be thinking that GitHub Copilot is a for writing code and you would right. However, it is a tool for writing content also.]]></summary></entry><entry><title type="html">What’s New with Visual Studio 2022</title><link href="https://www.josephguadagno.net/2021/12/23/whats-new-with-visual-studio-2022" rel="alternate" type="text/html" title="What’s New with Visual Studio 2022" /><published>2021-12-23T17:30:00-07:00</published><updated>2021-12-23T17:30:00-07:00</updated><id>https://www.josephguadagno.net/2021/12/23/whats-new-with-visual-studio-2022</id><content type="html" xml:base="https://www.josephguadagno.net/2021/12/23/whats-new-with-visual-studio-2022"><![CDATA[<p class="notice--info"><strong>Note:</strong> This post was originally posted on the Telerik <a href="https://www.telerik.com/blogs/whats-new-visual-studio-2022" target="_blank">blog</a>.</p>

<p>Visual Studio has had its first major release in about 18 months (depending on how you look at it) :smile:. This release adds a ton of new features and capabilities to the IDE. Now, is a great time to start learning about them.</p>

<h2 id="updated-user-interface">Updated User Interface</h2>

<p>The user experience of the IDE has been updated to be more consistent and more user friendly. This includes new icons, new fonts, new personalization, and more.</p>

<ul>
  <li><a href="https://github.com/microsoft/cascadia-code#welcome" target="_blank">Cascadia Code</a> font is now the default font for the editor.</li>
  <li>Visual Studio now integrates with <a href="https://accessibilityinsights.io/" target="_blank">Accessibility Insights</a> to help you find accessibility issues in your code.</li>
</ul>

<h3 id="new-icons">New Icons</h3>

<p>The icons have been updated to be consistent across different icons while remaining legible and familiar to the user.</p>

<p><img src="/assets/images/posts/vs2022-icon-refresh.png" alt="Visual Studio 2022 - Icon Refresh" class="align-center" /></p>

<h3 id="theme-support">Theme Support</h3>

<p>You’ve probably noticed in the image above that there are icons for a light and dark theme. While themes are not new to Visual Studio, Visual Studio now offers you the ability to sync your Visual Studio theme with your operating system theme.</p>

<p>The dark theme has been updated also to better align with the Microsoft design guidelines and improve accessibility.</p>

<p>Visual Studio now includes a <a href="https://github.com/microsoft/theme-converter-for-vs" target="_blank">Theme Converter</a> which converts Visual Studio Code themes to Visual Studio themes.</p>

<h3 id="inlay-hints">Inlay Hints</h3>

<p>Visual Studio now includes inlay hints for code completion, code lens, and more. Inlay hints can display parameter name hints for literals, function calls, and more.</p>

<p><img src="/assets/images/posts/vs2022-inlay-hints.png" alt="Visual Studio 2022 - Inlay Hints" class="align-center" /></p>

<p>In this image, you can see that Visual Studio tells you that the type for variable <code class="language-plaintext highlighter-rouge">imageUrl</code> is <code class="language-plaintext highlighter-rouge">string</code> and <code class="language-plaintext highlighter-rouge">contact</code> is of type <code class="language-plaintext highlighter-rouge">Contact</code>.  Further down the image, the <code class="language-plaintext highlighter-rouge">RedirectToAction</code> method has a parameter named <code class="language-plaintext highlighter-rouge">actionName</code> which this sample is using the <code class="language-plaintext highlighter-rouge">Details</code> action.</p>

<p>Note, this feature is not on by default. You can enable it by going to the <strong>Tools &gt; Options &gt; Text Editor &gt; C# or Basic &gt; Advanced</strong> then select <strong>Inlay Hints</strong>.</p>

<p><img src="/assets/images/posts/vs2022-inlay-options.png" alt="Visual Studio 2022 - Inlay Options" class="align-center" /></p>

<h2 id="speeding-up-visual-studio">Speeding up Visual Studio</h2>

<p>You might be saying that all these user interfaces are nice but Visual Studio is slow enough already. Well, that might have been the case for earlier versions of Visual Studio but that is not the case for Visual Studio 2022.  It’s faster in part that Visual Studio 2022 is now a 64-bit application. This means that the main process (devenv.exe) is no longer limited to 4GB of memory. Now Visual Studio can load larger projects and load more projects at once. You’ll also avoid the “Out of memory” errors that Visual Studio was seeing before when opening large solutions, files, or objects into memory.</p>

<p>Solution loading and file searching is now faster as well. Visual Studio now stores additional information about the solution in the .sln file. This information is used to speed up the loading of the solution. This information is also used to speed up the file searching.</p>

<p>To continue on the speeding up Visual Studio theme, Microsoft also improved the <em><a href="https://github.com/dotnet/project-system/blob/main/docs/up-to-date-check.md" target="_blank">Fast up to date</a></em> feature to better check to see if a project or it’s dependencies are up to date or need to be rebuilt.</p>

<h2 id="new-debugging-features">New Debugging Features</h2>

<p>Visual Studio 2022 has added and enhanced the debugging features of Visual Studio.</p>

<h3 id="breakpoints">Breakpoints</h3>

<p>Let’s talk about breakpoints first. There are two new breakpoints that you can set in Visual Studio, temporary and dependent breakpoints, as shown in the image below.</p>

<p><img src="/assets/images/posts/vs2022-debug-menu.png" alt="Visual Studio 2022 - Debug Breakpoint Menu" class="align-center" /></p>

<p>The <a href="https://docs.microsoft.com/en-us/visualstudio/debugger/using-breakpoints?view=vs-2022#BKMK_set_a_temporary_breakpoint?wt.mc_id=DT-MVP-4024623" target="_blank">Temporary breakpoint</a> is used to set a breakpoint that will only break once.  Once Visual Studio hits that breakpoint, it deletes it. This is helpful if you want to set a breakpoint only to validate that something is working, and you aren’t debugging the code.</p>

<p>The <a href="https://docs.microsoft.com/en-us/visualstudio/debugger/using-breakpoints?view=vs-2022#BKMK_set_a_dependent_breakpoint?wt.mc_id=DT-MVP-4024623" target="_blank">Dependent breakpoint</a> is used to set a breakpoint that will only break when another breakpoint is hit.</p>

<h3 id="force-run-to-cursor">Force Run To Cursor</h3>

<p>Previous versions of Visual Studio added a feature called “Run to Cursor”. This feature was used to execute code up to the code at the cursor.</p>

<p><img src="/assets/images/posts/vs2022-run-to-cursor.png" alt="Visual Studio 2022 - Run to Cursor" class="align-center" /></p>

<p>However, if you had any breakpoints between where you were and where you wanted to run to, Visual Studio would stop at all those breakpoints. Now with <em>Force Run To Cursor</em>, you can run to the cursor without hitting any breakpoints. If you hold the shift key down while hovering over the <em>Run to Cursor</em> glyph, Visual Studio will change the glyph to a <em>Force Run To Cursor</em> glyph and will run to the cursor without hitting any breakpoints.</p>

<p><img src="/assets/images/posts/vs2022-force-run-to-cursor.png" alt="Visual Studio 2022 - Force Run to Cursor" class="align-center" /></p>

<p>The <em>Force Run to Cursor</em> is also available in the <em>Debug</em> menu.</p>

<h3 id="other-debugging-features">Other Debugging Features</h3>

<p>For more on breakpoints or debugging tips and tricks in Visual Studio, check out this video:</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/otR7E-1Vg5s" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<h2 id="intellicode">IntelliCode</h2>

<p>IntelliCode improves IntelliSense by using AI to help you find the right code completion. IntelliCode is context aware and will help you find the right code completion when you are typing a method call, a property, or a variable.</p>

<p>In the image below, I start to create a new method after the <code class="language-plaintext highlighter-rouge">GetContactsAsync</code> method. After I type <code class="language-plaintext highlighter-rouge">public async</code>, IntelliCode is inferring that I want to create a <code class="language-plaintext highlighter-rouge">DeleteContactAsync</code> method with a parameter of type <code class="language-plaintext highlighter-rouge">contactId</code>. If that is what I want, I can hit the <code class="language-plaintext highlighter-rouge">Tab</code> key twice to insert the suggestion.</p>

<p><img src="/assets/images/posts/vs2022-intellicode-suggestion.png" alt="Visual Studio 2022 - IntelliCode suggestion" class="align-center" /></p>

<h2 id="git-support">Git Support</h2>

<p>Multiple repository support which includes the ability to track changes across all the repositories in a project. If you open a solution that has multiple Git repositories in it, Visual Studio will connect/activate those repositories. Right now, this is limited to a max of 10 repositories. You will be able to tell if Visual Studio has connected to or activated your different Git repositories by looking at the repository picker on the status bar (located at the lower right corner), which will tell you the number of active repositories you have.</p>

<p>The Git integration with Visual Studio has been improved and include support for multiple repositories, including improvements to both the Solution Explorer and Code Editors.</p>

<h2 id="hot-reload">Hot Reload</h2>

<p><a href="https://devblogs.microsoft.com/dotnet/update-on-net-hot-reload-progress-and-visual-studio-2022-highlights/?wt.mc_id=DT-MVP-4024623" target="_blank">Hot Reload</a> is a feature of Visual Studio that allows you to modify your applications managed code while that application is running without the need to hit a breakpoint or pause the application. This is a cool feature that will save you a lot of time without pausing or stopping your application to see how the source code changes you made changed your application. However, the support for this feature is still in progress.  There are some scenarios and products that are not yet supported.</p>

<h2 id="coming-soon-to-the-mac">Coming soon to the Mac</h2>

<p>Visual Studio 2022 for Mac is coming. The Visual Studio team wants to make a modern .NET IDE tailored for the Mac that will look familiar to those using Visual Studio for Windows while using native macOS UI. For more on the Visual Studio 2022 for Mac and/or to join the private beta, please visit <a href="https://devblogs.microsoft.com/visualstudio/join-the-visual-studio-2022-for-mac-private-preview" target="_blank">here</a>.</p>

<h2 id="bye-bye-net-5">Bye Bye .NET 5</h2>

<p>While technically not released with Visual Studio 2022, Microsoft released .NET 6 at the same time and includes the .NET 6 SDK in the Visual Studio installation. So now is the time to start migrating your .NET 5, and earlier, projects to .NET 6. As Barry Dorrans <a href="https://twitter.com/blowdart" target="_blank">@blowdart</a> points <a href="https://twitter.com/blowdart/status/1457819844858945537" target="_blank">out</a>, .NET 5 moves to end of life in May of 2022.</p>

<p>Some more details on the support policy for .NET.</p>

<h3 id="supported-versions">Supported Versions</h3>

<table>
  <thead>
    <tr>
      <th>Version</th>
      <th>Original Release Date</th>
      <th>Latest Patch Version</th>
      <th>Patch Release Date</th>
      <th>Support Level</th>
      <th>End of Support</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>.NET 6</td>
      <td>November 08, 2021</td>
      <td>6.0.0</td>
      <td>November 08, 2021</td>
      <td>LTS</td>
      <td>November 08, 2024</td>
    </tr>
    <tr>
      <td>.NET 5</td>
      <td>November 10, 2020</td>
      <td>5.0.12</td>
      <td>November 08, 2021</td>
      <td>Current</td>
      <td>May 08, 2022</td>
    </tr>
    <tr>
      <td>.NET Core 3.1</td>
      <td>December 3, 2019</td>
      <td>3.1.21</td>
      <td>November 08, 2021</td>
      <td>LTS</td>
      <td>December 3, 2022</td>
    </tr>
  </tbody>
</table>

<p><em>Source:</em> <a href="https://dotnet.microsoft.com/platform/support/policy/dotnet-core" target="_blank">.NET Support Policy</a></p>

<h2 id="wrap-up">Wrap Up</h2>

<p>So, what’s stopping you from upgrading your IDE and version of .NET?</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term=".NET" /><category term="Visual Studio" /><category term="Visual Studio 2022" /><category term="dotnet" /><summary type="html"><![CDATA[Note: This post was originally posted on the Telerik blog.]]></summary></entry><entry><title type="html">What Was Added To C# 10</title><link href="https://www.josephguadagno.net/2021/12/02/what-was-added-to-c-10" rel="alternate" type="text/html" title="What Was Added To C# 10" /><published>2021-12-02T21:30:00-07:00</published><updated>2021-12-02T21:30:00-07:00</updated><id>https://www.josephguadagno.net/2021/12/02/what-was-added-to-c-10</id><content type="html" xml:base="https://www.josephguadagno.net/2021/12/02/what-was-added-to-c-10"><![CDATA[<p class="notice--info"><strong>Note:</strong> This post was originally posted on the Telerik <a href="https://www.telerik.com/blogs/what-was-added-csharp-10" target="_blank">blog</a></p>

<p>In a previous <a href="https://www.telerik.com/blogs/what-i-like-about-csharp-9" target="_blank">post</a>, I talked about all of the new features of C# 9. With the release of .NET 6 recently, I wanted to share some of the new language features of C# 10.</p>

<p>Let’s take a look at some of the new language features.</p>

<h2 id="saving-time">Saving Time</h2>

<p>.NET added quite a few features to the language that can save you a lot of time.</p>

<h3 id="file-scoped-namespaces">File-Scoped Namespaces</h3>

<p>In my opinion, file-scoped namespaces are a great way to organize your code. They allow you to organize your code into logical groups and keep your code from being too cluttered.</p>

<p>File-Scoped namespaces allow you to save some keystrokes and indentation in your code. Now, you can declare your namespace at the top of your file, assuming you only have one namespace for your file. Which I believe you should always do.</p>

<p>Old Code:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="k">namespace</span> <span class="nn">MyNamespace</span>
<span class="p">{</span>
  <span class="k">class</span> <span class="nc">MyClass</span>
  <span class="p">{</span>
    <span class="k">public</span> <span class="k">void</span> <span class="nf">MyMethod</span><span class="p">()</span>
    <span class="p">{</span>
        <span class="c1">// ...</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now becomes:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">namespace</span> <span class="nn">MyNamespace</span><span class="p">;</span>

<span class="k">class</span> <span class="nc">MyClass</span>
<span class="p">{</span>
  <span class="k">public</span> <span class="k">void</span> <span class="nf">MyMethod</span><span class="p">()</span>
  <span class="p">{</span>
      <span class="c1">// ...</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now we save 2 curly braces and 1 indentation level. I kind of wish this feature was in .NET 1, since you really should only have one namespace per file :smile:.</p>

<h3 id="global-using-directives">Global Using Directives</h3>

<p>How often do you see or type the same namespaces over and over again? <code class="language-plaintext highlighter-rouge">using System;</code>, for me, is declared in almost every file in my project. With C# 10s {:target=”_blank”} you can declare your using directives at the top of your file and then use them throughout your file. Now I can add <code class="language-plaintext highlighter-rouge">global using System;</code> to one file in my project, and the <code class="language-plaintext highlighter-rouge">using</code> statement will be <em>referenced</em> throughout all my files/classes.</p>

<p>I see myself using the following code in my project regularly now:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">global</span> <span class="k">using</span> <span class="nn">System</span><span class="p">;</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span>
<span class="k">global</span> <span class="k">using</span> <span class="nn">System.Linq</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>While not required, I recommend that you place all of your global using directives in a standard filename across your projects.  I plan on using <code class="language-plaintext highlighter-rouge">GlobalUsings.cs</code> but feel free to use whatever you want.</p>

<p>If putting your <code class="language-plaintext highlighter-rouge">global using</code> directives in a file is not your thing, you can also add then to your <code class="language-plaintext highlighter-rouge">.csproj</code> file. If I wanted to include the three <code class="language-plaintext highlighter-rouge">global using</code> directives above in my <code class="language-plaintext highlighter-rouge">.csproj</code> file, I would add the following to my <code class="language-plaintext highlighter-rouge">.csproj</code> file:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="p">&lt;</span><span class="n">ItemGroup</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="n">Using</span> <span class="n">Include</span><span class="p">=</span><span class="s">"System"</span> <span class="p">/&gt;</span>
  <span class="p">&lt;</span><span class="n">Using</span> <span class="n">Include</span><span class="p">=</span><span class="s">"System.Collections.Generic"</span> <span class="p">/&gt;</span>
  <span class="p">&lt;</span><span class="n">Using</span> <span class="n">Include</span><span class="p">=</span><span class="s">"System.Linq"</span> <span class="p">/&gt;</span>
<span class="p">&lt;/</span><span class="n">ItemGroup</span><span class="p">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Either approach will work, but the <code class="language-plaintext highlighter-rouge">.csproj</code> approach seems to be easier to discover.</p>

<p>If <code class="language-plaintext highlighter-rouge">global using</code> is not your or your teams thing, you can disable it by adding the following to your <code class="language-plaintext highlighter-rouge">.csproj</code> file:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="p">&lt;</span><span class="n">PropertyGroup</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="n">ImplicitUsings</span><span class="p">&gt;</span><span class="n">disable</span><span class="p">&lt;/</span><span class="n">ImplicitUsings</span><span class="p">&gt;</span> <span class="c1">// Can also be set to `false`</span>
<span class="p">&lt;/</span><span class="n">PropertyGroup</span><span class="p">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="extended-property-patterns">Extended Property Patterns</h3>

<p><a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns?wt.mc_id=DT-MVP-4024623" target="_blank">Pattern Matching</a> was introduced in C# 7. It allows you to match the properties of an object against a pattern. Pattern matching is a great way to write cleaner code. In C# 8, the <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/pattern-matching?wt.mc_id=DT-MVP-4024623" target="_blank">Property Patterns</a> feature was added, which enabled you to match against properties of an object like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="n">Person</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Person</span> <span class="p">{</span>
  <span class="n">FirstName</span> <span class="p">=</span> <span class="s">"Joe"</span><span class="p">,</span>
  <span class="n">LastName</span> <span class="p">=</span> <span class="s">"Guadagno"</span><span class="p">,</span>
  <span class="n">Address</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Address</span> <span class="p">{</span>
    <span class="n">City</span> <span class="p">=</span> <span class="s">"Chandler"</span><span class="p">,</span>
    <span class="n">State</span> <span class="p">=</span> <span class="s">"AZ"</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Other code</span>

<span class="k">if</span> <span class="p">(</span><span class="n">person</span> <span class="k">is</span> <span class="n">Person</span> <span class="p">{</span><span class="n">Address</span><span class="p">:</span> <span class="p">{</span><span class="n">State</span><span class="p">:</span> <span class="s">"AZ"</span><span class="p">}})</span>
<span class="p">{</span>
  <span class="c1">// Do something</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now with C# 10, you can reference nested properties of objects with dot notation.  For example, you can match against the <code class="language-plaintext highlighter-rouge">City</code> and <code class="language-plaintext highlighter-rouge">State</code> properties of a <code class="language-plaintext highlighter-rouge">Person</code> object like this:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">if</span> <span class="p">(</span><span class="n">person</span> <span class="k">is</span> <span class="n">Person</span> <span class="p">{</span><span class="n">Address</span><span class="p">.</span><span class="n">State</span><span class="p">:</span> <span class="s">"AZ"</span><span class="p">})</span>
<span class="p">{</span>
  <span class="c1">// Do something</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="string-improvements">String Improvements</h2>

<p>C# 10 made improvements to interpolated strings in C# 10. <code class="language-plaintext highlighter-rouge">const</code> variables can now be used with interpolated strings.</p>

<p>I have trouble finding a “<em>real world</em>” example of this, so here is an example of how it works:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">const</span> <span class="kt">string</span> <span class="n">greeting</span> <span class="p">=</span> <span class="s">"Hello"</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">string</span> <span class="n">name</span> <span class="p">=</span> <span class="s">"Joe"</span><span class="p">;</span>
<span class="k">const</span> <span class="kt">string</span> <span class="n">message</span> <span class="p">=</span> <span class="s">$"</span><span class="p">{</span><span class="n">greeting</span><span class="p">}</span><span class="s">, </span><span class="p">{</span><span class="n">name</span><span class="p">}</span><span class="s">!"</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">message</code> variable will be the value of <code class="language-plaintext highlighter-rouge">Hello, Joe!</code>.</p>

<p>Interpolated has not just been improved for <code class="language-plaintext highlighter-rouge">const</code>s but for variables that can be determined at compile time.  Let’s say you maintain a library, and you decide to obsolete a method named <code class="language-plaintext highlighter-rouge">OldMethod</code>. In the past, you would have to do something like this:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">MyClass</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">Obsolete</span><span class="p">(</span><span class="s">$"Use NewMethod instead"</span><span class="p">,</span> <span class="k">true</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">void</span> <span class="nf">OldMethod</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">NewMethod</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>But now, you can do this:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">MyClass</span>
<span class="p">{</span>
    <span class="p">[</span><span class="nf">Obsolete</span><span class="p">(</span><span class="s">$"Use </span><span class="p">{</span><span class="k">nameof</span><span class="p">(</span><span class="n">NewMethod</span><span class="p">)}</span><span class="s"> instead"</span><span class="p">,</span> <span class="k">true</span><span class="p">)]</span>
    <span class="k">public</span> <span class="k">void</span> <span class="nf">OldMethod</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>

    <span class="k">public</span> <span class="k">void</span> <span class="nf">NewMethod</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This makes it easier to update your code when you need to. Now you don’t have to remember everywhere you used <em>hardcoded</em> name of the method you want to obsolete.</p>

<h2 id="callerargumentexpression">CallerArgumentExpression</h2>

<p><code class="language-plaintext highlighter-rouge">CallerArgumentExpression</code> attribute is a new feature of C# 10 that enables you to capture the expression that is passed into a method which is useful for debugging purposes.</p>

<p>Let’s say we have a method called <code class="language-plaintext highlighter-rouge">IsValid</code> that checks and validates assorted properties of a <code class="language-plaintext highlighter-rouge">Person</code> object.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">Validation</span> <span class="p">{</span>
  <span class="k">public</span> <span class="k">static</span> <span class="n">book</span> <span class="nf">IsValid</span><span class="p">(</span><span class="n">Person</span> <span class="n">person</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="n">Debug</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(</span><span class="n">person</span> <span class="p">!=</span> <span class="k">null</span><span class="p">);</span>
    <span class="n">Debug</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">FirstName</span><span class="p">));</span>
    <span class="n">Debug</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">LastName</span><span class="p">));</span>
    <span class="n">Debug</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">Address</span><span class="p">.</span><span class="n">City</span><span class="p">));</span>
    <span class="n">Debug</span><span class="p">.</span><span class="nf">Assert</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">Age</span> <span class="p">&gt;</span> <span class="m">18</span><span class="p">);</span>
    <span class="k">return</span> <span class="k">true</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now we have the following code that calls the <code class="language-plaintext highlighter-rouge">IsValid</code> method:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="n">Person</span> <span class="n">person</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="n">Validation</span><span class="p">.</span><span class="nf">IsValid</span><span class="p">(</span><span class="n">person</span><span class="p">);</span> <span class="c1">// Fails: person != null</span>

<span class="n">Person</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Person</span><span class="p">{</span>
  <span class="n">FirstName</span> <span class="p">=</span> <span class="s">"Joe"</span><span class="p">,</span>
  <span class="n">LastName</span> <span class="p">=</span> <span class="s">"Guadagno"</span><span class="p">,</span>
  <span class="n">Address</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Address</span> <span class="p">{</span>
    <span class="n">City</span> <span class="p">=</span> <span class="s">"Chandler"</span><span class="p">,</span>
    <span class="n">State</span> <span class="p">=</span> <span class="s">"AZ"</span>
  <span class="p">},</span>  
  <span class="n">Age</span> <span class="p">=</span> <span class="m">17</span>
<span class="p">};</span>
<span class="n">result</span> <span class="p">=</span> <span class="n">Validation</span><span class="p">.</span><span class="nf">IsValid</span><span class="p">(</span><span class="n">person</span><span class="p">);</span> <span class="c1">// Fails: person.Age &gt; 18</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Each call to will fail because at least one assertion fails. But which one failed? That is where <code class="language-plaintext highlighter-rouge">CallerArgumentExpression</code> comes into play. To fix this, we’ll create a custom <code class="language-plaintext highlighter-rouge">Assert</code> method and add the <code class="language-plaintext highlighter-rouge">CallerArgumentExpression</code> attribute to the method:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">Assert</span><span class="p">(</span><span class="kt">bool</span> <span class="n">condition</span><span class="p">,</span> <span class="p">[</span><span class="nf">CallerArgumentExpression</span><span class="p">(</span><span class="s">"condition"</span><span class="p">)]</span> <span class="kt">string</span> <span class="n">expression</span> <span class="p">=</span> <span class="k">default</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">if</span> <span class="p">(!</span><span class="n">condition</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">$"Condition failed: </span><span class="p">{</span><span class="n">expression</span><span class="p">}</span><span class="s">"</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now if we call the <code class="language-plaintext highlighter-rouge">Validate</code> method with the above sample, we’ll get the following output</p>

<p><code class="language-plaintext highlighter-rouge">Condition failed: person != null</code></p>

<p>and</p>

<p><code class="language-plaintext highlighter-rouge">Condition failed: person.Age &gt; 18)</code></p>

<h3 id="null-argument-checks">Null Argument Checks</h3>

<p>The introduction of <code class="language-plaintext highlighter-rouge">CallerArgumentExpression</code> attribute has enabled a few new extensions methods to the framework. For example, there is now a <code class="language-plaintext highlighter-rouge">ThrowIfNull</code> extension method that can be used to throw an <code class="language-plaintext highlighter-rouge">ArgumentNullException</code> if the argument is null.</p>

<p>We no longer have to write this:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">if</span> <span class="p">(</span><span class="n">argument</span> <span class="k">is</span> <span class="k">null</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentNullException</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">argument</span><span class="p">));</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>We can now write this:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">ArgumentNullException</span><span class="p">.</span><span class="nf">ThrowIfNull</span><span class="p">(</span><span class="n">argument</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The method, behind the scenes, looks like this:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">ThrowIfNull</span><span class="p">(</span>
    <span class="p">[</span><span class="n">NotNull</span><span class="p">]</span> <span class="kt">object</span><span class="p">?</span> <span class="n">argument</span><span class="p">,</span>
    <span class="p">[</span><span class="nf">CallerArgumentExpression</span><span class="p">(</span><span class="s">"argument"</span><span class="p">)]</span> <span class="kt">string</span><span class="p">?</span> <span class="n">paramName</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">argument</span> <span class="k">is</span> <span class="k">null</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentNullException</span><span class="p">(</span><span class="n">paramName</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="wrap-up">Wrap Up</h2>

<p>This is not an exhaustive list of new language features introduced in C# 10. To see what else was added to C# 10, check out <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10?wt.mc_id=DT-MVP-4024623">What’s new in C# 10.0</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term=".NET" /><category term="Namespaces" /><category term="Interpolation" /><category term="CallerArgumentExpression" /><category term="Pattern Matching" /><category term="dotnet" /><category term="csharp" /><summary type="html"><![CDATA[Note: This post was originally posted on the Telerik blog]]></summary></entry><entry><title type="html">What I Like About C# 9</title><link href="https://www.josephguadagno.net/2021/08/23/what-i-like-about-c-9" rel="alternate" type="text/html" title="What I Like About C# 9" /><published>2021-08-23T07:30:00-07:00</published><updated>2021-08-23T07:30:00-07:00</updated><id>https://www.josephguadagno.net/2021/08/23/what-i-like-about-c#-9</id><content type="html" xml:base="https://www.josephguadagno.net/2021/08/23/what-i-like-about-c-9"><![CDATA[<p class="notice--info"><strong>Note:</strong> This post was originally posted on the Telerik <a href="https://www.telerik.com/blogs/what-i-like-about-csharp-9" target="_blank">blog</a>.</p>

<p>I’ve been a software engineer for 20+ years, and as the adage goes, <em>You can’t teach an old dog new tricks</em>. However, if there is one thing I learned in the 20+ years is that I am <strong><em>ALWAYS learning</em></strong>. There are always new technologies coming out, new languages, and new products to solve complex problems. .NET 5 introduced C# 9, which had many new language features. So it was time for me to learn some new tricks and I dove into .NET 5’s C# 9 language additions.</p>

<p>After using these new language features, keywords, and syntax, I noticed that they started to save me keystrokes and time. Since these language additions helped me I wanted to share them with you.</p>

<p>Let’s take a look at some of the new language features.</p>

<h2 id="records">Records</h2>

<p>The new <code class="language-plaintext highlighter-rouge">record</code> keyword defines a reference type that provides some built-in functionality for representing data. You might be thinking that this sounds a lot like a <code class="language-plaintext highlighter-rouge">class</code>, and you would be correct. It does. However, the intent is to provide smaller and more concise types to represent immutable data. I like to think of them as a type used primarily to transfer data and not have a lot of methods or data manipulation.</p>

<p>More on C# 9 <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#record-types?wt.mc_id=DT-MVP-4024623" target="_blank">records</a>.</p>

<h3 id="defining-a-record">Defining a Record</h3>

<p>There are a few different ways to define a <code class="language-plaintext highlighter-rouge">record</code>. The simplest form is:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">record</span> <span class="nf">Person</span><span class="p">(</span><span class="kt">string</span> <span class="n">FirstName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">LastName</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>At first glance, at least for me, that seemed weird.  It has a method look and feel.  There is even a semicolon at the end.  But, the above line creates a <em>Person</em> type with the read/write properties of <code class="language-plaintext highlighter-rouge">FirstName</code> and <code class="language-plaintext highlighter-rouge">LastName</code>. You can access the <em>Person</em> as follows:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Person</span><span class="p">(</span><span class="s">"Joseph"</span><span class="p">,</span> <span class="s">"Guadagno"</span><span class="p">);</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">FirstName</span><span class="p">);</span> <span class="c1">// Outputs Joseph</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">person</span><span class="p">.</span><span class="n">LastName</span><span class="p">);</span> <span class="c1">// Outputs Guadagno</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>So far, this looks very <code class="language-plaintext highlighter-rouge">class</code>-like. Well, it is, except for the declaration. We already saved a bunch of keystrokes.  But let’s dig more into it.</p>

<p>Another way to define the <em>Person</em> <code class="language-plaintext highlighter-rouge">record</code> is more <code class="language-plaintext highlighter-rouge">class</code>-like:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">record</span> <span class="n">Person</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">FirstName</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="k">public</span> <span class="kt">string</span> <span class="n">LastName</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>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="creating-records-by-position">Creating Records by Position</h3>

<p>You can further reduce some typing and remove some boilerplate code using the new positional syntax for records. For example, if you wanted to declare a variable with the class approach and initialize it with data, you would do something like this.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Person</span> <span class="p">{</span> <span class="n">FirstName</span> <span class="p">=</span> <span class="s">"Joseph"</span><span class="p">,</span> <span class="n">LastName</span><span class="p">=</span><span class="s">"Guadagno"</span><span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>With positional syntax, that would look like this.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Person</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="p">(</span><span class="s">"Joseph"</span><span class="p">,</span> <span class="s">"Guadagno"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>That’s 26 fewer characters. Behind the scenes the compiling is creating a lot of the <em>boilerplate</em> code for you. The compiler creates a constructor that matches the position of the record declaration.  Since the <code class="language-plaintext highlighter-rouge">FirstName</code> property was the first property declared when we defined the method, it assumes that the <em>Joseph</em> value should be the value of the <code class="language-plaintext highlighter-rouge">FirstName</code> property. The compiler also generated all the properties as init-only, more on that <a href="#defining-set-once-properties">later</a>, meaning the properties can not get set after initialization making them read only.</p>

<h3 id="value-equality">Value Equality</h3>

<p>One set of built-in functionality that records provide is value equality. When checking to see if two records are equal, it will look at the values of each of the properties and not the reference.</p>

<p>Assuming the definition of.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">record</span> <span class="nf">Person</span><span class="p">(</span><span class="kt">string</span> <span class="n">FirstName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">LastName</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>When comparing records</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="n">Person</span> <span class="n">person1</span> <span class="p">=</span> <span class="k">new</span> <span class="p">(</span><span class="s">"Joseph"</span><span class="p">,</span> <span class="s">"Guadagno"</span><span class="p">);</span>
<span class="n">Person</span> <span class="n">person2</span> <span class="p">=</span> <span class="k">new</span> <span class="p">(</span><span class="s">"Joseph"</span><span class="p">,</span> <span class="s">"Guadagno"</span><span class="p">);</span>

<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">person1</span> <span class="p">==</span> <span class="n">person2</span><span class="p">);</span> <span class="c1">// outputs True</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="nf">ReferenceEquals</span><span class="p">(</span><span class="n">person1</span><span class="p">,</span> <span class="n">person2</span><span class="p">));</span> <span class="c1">// outputs False</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Since <code class="language-plaintext highlighter-rouge">person2</code> has the same <code class="language-plaintext highlighter-rouge">FirstName</code> and <code class="language-plaintext highlighter-rouge">LastName</code> of <code class="language-plaintext highlighter-rouge">person2</code> they are equal, although the references are not.</p>

<h3 id="improved-tostring">Improved ToString()</h3>

<p>Using the <code class="language-plaintext highlighter-rouge">record</code> keyword, gets you another built in method.  What a deal!  An improved <code class="language-plaintext highlighter-rouge">ToString</code> method.  I really wish this was opt-in standard for classes to.</p>

<p>The <code class="language-plaintext highlighter-rouge">ToString</code> method outputs the following format.</p>

<pre><code class="language-txt">&lt;record type name&gt; { &lt;property name&gt; = &lt;value&gt;, &lt;property name&gt; = &lt;value&gt;, ...}
</code></pre>

<p>For a record defined as</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">record</span> <span class="nf">Person</span><span class="p">(</span><span class="kt">string</span> <span class="n">FirstName</span><span class="p">,</span> <span class="kt">string</span> <span class="n">LastName</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>and initialized as</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Person</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="p">{</span><span class="s">"Joseph"</span><span class="p">,</span> <span class="s">"Guadagno"</span><span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>the <code class="language-plaintext highlighter-rouge">ToString</code> method would output a string like</p>

<pre><code class="language-txt">Person { FirstName = Joseph, LastName = Guadagno }
</code></pre>

<p>If there is a reference type as one of the properties of the record, the records <code class="language-plaintext highlighter-rouge">ToString</code> implementation will output the type name of it.</p>

<p><strong><em>NOTE</em></strong> Don’t try to use the <code class="language-plaintext highlighter-rouge">ToString</code> method to determine the records properties.</p>

<h3 id="inheriting-records">Inheriting Records</h3>

<p>Records can be inherited the same way classes are except for the following:</p>

<ul>
  <li>Records can’t inherit from a class</li>
  <li>Class can’t inherit from a record</li>
  <li>When comparing records, the type of record is used as part of the comparison and not just the values.</li>
</ul>

<h3 id="copying-records">Copying Records</h3>

<p>Copying records is pretty easy. As an added bonus, the syntax makes the code easier to read.</p>

<p>Let’s say I had a <em>Person</em> record defined as.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">record</span> <span class="n">Person</span>
<span class="p">{</span>
    <span class="kt">string</span> <span class="n">FirstName</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="kt">string</span> <span class="n">LastName</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="kt">string</span> <span class="n">HomeState</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>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Let’s also say I want to create one <em>Person</em> and make multiple copies and just change a few properties. As if I was to create variables for the whole family.  In our case, the <code class="language-plaintext highlighter-rouge">LastName</code> and <code class="language-plaintext highlighter-rouge">HomeState</code> properties are the same and using records along with the <code class="language-plaintext highlighter-rouge">with</code> keyword makes this easier.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">me</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Person</span><span class="p">(</span><span class="s">"Joseph"</span><span class="p">,</span> <span class="s">"Guadagno"</span><span class="p">,</span> <span class="s">"Arizona"</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">wife</span> <span class="p">=</span> <span class="n">me</span> <span class="n">with</span> <span class="p">{</span><span class="n">FirstName</span> <span class="p">=</span> <span class="s">"Deidre"</span><span class="p">};</span>
<span class="kt">var</span> <span class="n">son</span> <span class="p">=</span> <span class="n">me</span> <span class="n">with</span> <span class="p">{</span><span class="n">FirstName</span> <span class="p">=</span> <span class="s">"Joseph Jr."</span><span class="p">};</span>
<span class="kt">var</span> <span class="n">daughter</span> <span class="p">=</span> <span class="n">me</span> <span class="n">with</span> <span class="p">{</span><span class="n">FirstName</span> <span class="p">=</span> <span class="s">"Emily"</span><span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now, the <code class="language-plaintext highlighter-rouge">wife</code>, <code class="language-plaintext highlighter-rouge">son</code>, and <code class="language-plaintext highlighter-rouge">daughter</code> objects have the property of <code class="language-plaintext highlighter-rouge">LastName</code> set to <em>Guadagno</em> and <code class="language-plaintext highlighter-rouge">HomeState</code> set to <em>Arizona</em>.</p>

<h2 id="defining-set-once-properties">Defining Set Once Properties</h2>

<p>You can also use the new <code class="language-plaintext highlighter-rouge">init</code> keyword to make certain properties settable on initialization only. The <code class="language-plaintext highlighter-rouge">init</code> keyword works with properties or indexers in <code class="language-plaintext highlighter-rouge">struct</code>, <code class="language-plaintext highlighter-rouge">class</code>, or <code class="language-plaintext highlighter-rouge">record</code>.</p>

<p>Let’s say with want to define a <em>Person</em> <code class="language-plaintext highlighter-rouge">record</code> with <code class="language-plaintext highlighter-rouge">FirstName</code>, <code class="language-plaintext highlighter-rouge">LastName</code>, and <code class="language-plaintext highlighter-rouge">CreateOnDate</code> properties.  The <code class="language-plaintext highlighter-rouge">CreatedOnDate</code> should not be editable after the record is initialized. We would declare the <code class="language-plaintext highlighter-rouge">record</code> like this.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">record</span> <span class="n">Person</span> 
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">FirstName</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="k">public</span> <span class="kt">string</span> <span class="n">LastName</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="k">public</span> <span class="n">DateTime</span> <span class="n">CreatedOnDate</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="n">init</span><span class="p">;}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You see on line 5, we have the keyword <code class="language-plaintext highlighter-rouge">init</code> instead of <code class="language-plaintext highlighter-rouge">set</code>.  This means the <code class="language-plaintext highlighter-rouge">CreatedOnDate</code> can only be set when initialized.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Person</span><span class="p">(</span><span class="s">"Joseph"</span><span class="p">,</span> <span class="s">"Guadagno"</span><span class="p">,</span> <span class="n">DateTime</span><span class="p">.</span><span class="nf">Now</span><span class="p">());</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>After declaring this record, we are limited as to what properties we can change.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">person</span><span class="p">.</span><span class="n">FirstName</span> <span class="p">=</span> <span class="s">"Joe"</span><span class="p">;</span> <span class="c1">// valid</span>
<span class="n">person</span><span class="p">.</span><span class="n">CreatedOnDate</span> <span class="p">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="nf">Now</span><span class="p">();</span> <span class="c1">// You will get a compile error</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Line 2, will cause a compilation error because the property <code class="language-plaintext highlighter-rouge">CreatedOnDate</code> was set to init-only.</p>

<h3 id="alternative-declaration">Alternative declaration</h3>

<p>You can also declare the setter of a property with a backing field as init-only.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">Person</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">DateTime</span> <span class="n">_dateOfBirth</span><span class="p">;</span>
    <span class="k">public</span> <span class="n">DateTime</span> <span class="n">DateOfBirth</span> 
    <span class="p">{</span>
        <span class="k">get</span> <span class="p">=&gt;</span> <span class="n">_dateOfBirth</span><span class="p">;</span>
        <span class="n">init</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="k">value</span> <span class="p">??</span> <span class="k">throw</span> <span class="k">new</span> <span class="nf">ArgumentNullException</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">DateOfBirth</span><span class="p">)));</span>
    <span class="p">}</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">FirstName</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="k">public</span> <span class="kt">string</span> <span class="n">LastName</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>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>On line 7, we define the class <em>Person</em> with an init only property <code class="language-plaintext highlighter-rouge">DateOfBirth</code> that must be set at initialization or you will get a compile error or runtime exception depending on the implementation.</p>

<p>This is valid (assuming the definition above).</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Person</span><span class="p">{</span><span class="n">FirstName</span><span class="p">=</span><span class="s">"Joseph"</span><span class="p">,</span> <span class="n">LastName</span><span class="p">=</span><span class="s">"Guadagno"</span><span class="p">,</span> <span class="n">DateOfBirth</span><span class="p">=</span><span class="n">DateTime</span><span class="p">.</span><span class="nf">Now</span><span class="p">()};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This is not (assuming the definition above).</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">person</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Person</span><span class="p">{</span><span class="n">FirstName</span><span class="p">=</span><span class="s">"Joseph"</span><span class="p">,</span> <span class="n">LastName</span><span class="p">=</span><span class="s">"Guadagno"</span><span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Based on the above definition, this code sample will throw a runtime exception.</p>

<h2 id="top-level-statements">Top Level Statements</h2>

<p>I started out this post introducing the notion that C# 9’s language features help you be more productive and reduce keystrokes. Top Level statements is another one of the features.  To be honest, you probably won’t use this feature a lot.  In fact, you can only have one file in your application that uses this feature.  It’s generally helpful for demonstrating some functionality and removing all of the extra ceremony around the application startup.  I see myself using it when I am creating presentations.</p>

<p>Let’s take the typical “Hello World” sample.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="k">using</span> <span class="nn">System</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">CSharp9Features.ConsoleApp</span>
<span class="p">{</span>
    <span class="k">static</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="k">static</span> <span class="k">void</span> <span class="nf">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>
            <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Hello World!"</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>It’s 12 lines long using the default .NET C# console app template.  Now with top level statements, this can reduced to.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">System</span><span class="p">.</span><span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now “<em>we</em>” reduced the code from 12 lines and 210 characters to 1 line and 40 characters.</p>

<p>Behind the scenes the compiler essentially created the 12 lines and 210 characters for you.  But again, C#9 is trying to make things easier for you so why type those lines when the compiler knows that is what you want.</p>

<p>In a more “realistic” example, let’s say for an ASP.NET Core WebAPI project.  The typical template would have a <code class="language-plaintext highlighter-rouge">Program.cs</code> file that looks something like this.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="rouge-code"><pre><span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Contacts.Api</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">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>
            <span class="nf">CreateHostBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">).</span><span class="nf">Build</span><span class="p">().</span><span class="nf">Run</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">IHostBuilder</span> <span class="nf">CreateHostBuilder</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">=&gt;</span>
            <span class="n">Host</span><span class="p">.</span><span class="nf">CreateDefaultBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">ConfigureWebHostDefaults</span><span class="p">(</span><span class="n">webBuilder</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="n">webBuilder</span><span class="p">.</span><span class="n">UseStartup</span><span class="p">&lt;</span><span class="n">Startup</span><span class="p">&gt;();</span> <span class="p">})</span>
                <span class="p">.</span><span class="nf">ConfigureLogging</span><span class="p">(</span><span class="n">logging</span> <span class="p">=&gt;</span>
                <span class="p">{</span>
                    <span class="n">logging</span><span class="p">.</span><span class="nf">ClearProviders</span><span class="p">();</span>
                    <span class="n">logging</span><span class="p">.</span><span class="nf">SetMinimumLevel</span><span class="p">(</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">Trace</span><span class="p">);</span>
                <span class="p">});</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now with C# 9, I can remove some of the noise and ceremony and have my code just be what my API needs to start.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="k">using</span> <span class="nn">Contacts.Api</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Hosting</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.Logging</span><span class="p">;</span>

<span class="nf">CreateHostBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">).</span><span class="nf">Build</span><span class="p">().</span><span class="nf">Run</span><span class="p">();</span>

<span class="n">IHostBuilder</span> <span class="nf">CreateHostBuilder</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">=&gt;</span>
<span class="n">Host</span><span class="p">.</span><span class="nf">CreateDefaultBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">ConfigureWebHostDefaults</span><span class="p">(</span><span class="n">webBuilder</span> <span class="p">=&gt;</span> <span class="p">{</span> <span class="n">webBuilder</span><span class="p">.</span><span class="n">UseStartup</span><span class="p">&lt;</span><span class="n">Startup</span><span class="p">&gt;();</span> <span class="p">})</span>
    <span class="p">.</span><span class="nf">ConfigureLogging</span><span class="p">(</span><span class="n">logging</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">logging</span><span class="p">.</span><span class="nf">ClearProviders</span><span class="p">();</span>
        <span class="n">logging</span><span class="p">.</span><span class="nf">SetMinimumLevel</span><span class="p">(</span><span class="n">LogLevel</span><span class="p">.</span><span class="n">Trace</span><span class="p">);</span>
    <span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This code now clearly states what the intent of the <code class="language-plaintext highlighter-rouge">program.cs</code> is without the <em>extra</em> <code class="language-plaintext highlighter-rouge">namespace</code> or <code class="language-plaintext highlighter-rouge">Main</code> method.</p>

<h2 id="new-pattern-matching">New Pattern Matching</h2>

<p>While <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns?wt.mc_id=DT-MVP-4024623" target="_blank">pattern matching</a> is not new to C# 9, C# 9 did add a few more patterns.</p>

<p>Logical patterns:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">and</code></li>
  <li><code class="language-plaintext highlighter-rouge">or</code></li>
  <li><code class="language-plaintext highlighter-rouge">not</code></li>
</ul>

<p>Relations patterns:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">&lt;</code></li>
  <li><code class="language-plaintext highlighter-rouge">&gt;</code></li>
  <li><code class="language-plaintext highlighter-rouge">&lt;=</code></li>
  <li><code class="language-plaintext highlighter-rouge">&gt;=</code></li>
</ul>

<p>These patterns help add readability to code. My favorite addition to this is the <code class="language-plaintext highlighter-rouge">not</code> pattern matcher.  Now I can take all the instances of</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">if</span> <span class="p">(!</span><span class="n">person</span> <span class="k">is</span> <span class="k">null</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>and make them more readable with</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">if</span> <span class="p">(</span><span class="n">person</span> <span class="k">is</span> <span class="n">not</span> <span class="k">null</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>While this one is more keystrokes, the extra couple of characters makes it more readable to me than the <code class="language-plaintext highlighter-rouge">!</code> operator.</p>

<h2 id="omitting-the-type">Omitting the type</h2>

<p>The compiler is getting smarter. It’s not necessarily getting more intelligent, but getting better at understanding what you are trying to do and, again, reducing the keystrokes. The C# 9 feature of target-typed new expressions demonstrates that the compiler is getting smarter. Now, based on the variable declaration or method signature, you can omit the type in variable declarations or usage.</p>

<p>Here we are declaring a variable <code class="language-plaintext highlighter-rouge">_people</code> of type <code class="language-plaintext highlighter-rouge">List&lt;Person&gt;</code></p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">private</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Person</span><span class="p">&gt;</span> <span class="n">_people</span> <span class="p">=</span> <span class="k">new</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You no longer have to initialize the variable of <code class="language-plaintext highlighter-rouge">_people</code> with <code class="language-plaintext highlighter-rouge">new List&lt;Person&gt;()</code>. The compiler can assume that you want a new List of Person.</p>

<p>The same goes for methods. In the sample below, the method <code class="language-plaintext highlighter-rouge">CalculateSalary</code> expects a parameter of type  <code class="language-plaintext highlighter-rouge">PerformanceRating</code>.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">Person</span> <span class="nf">CalculateSalary</span><span class="p">(</span><span class="n">PerformanceRating</span> <span class="n">rating</span><span class="p">)</span> 
<span class="p">{</span>  
    <span class="c1">// Omitted</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>If I wanted to initialize an new <code class="language-plaintext highlighter-rouge">PerformanceRating</code> object for the method without creating a variable, I can now.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">person</span> <span class="p">=</span> <span class="n">person</span><span class="p">.</span><span class="nf">CalculateSalary</span><span class="p">(</span><span class="k">new</span> <span class="p">());</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>or, I can pass in a new <code class="language-plaintext highlighter-rouge">PerformanceRating</code> object with one or more of it’s properties initialized.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">person</span> <span class="p">=</span> <span class="n">person</span><span class="p">.</span><span class="nf">CalculateSalary</span><span class="p">(</span><span class="k">new</span> <span class="p">()</span> <span class="p">{</span><span class="n">Rating</span> <span class="p">=</span><span class="s">"Rock Star"</span><span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This syntax does take some getting used to. I think in the long it leads to code that is easier to use.  However, it might add more fuel to the <code class="language-plaintext highlighter-rouge">var</code> vs. typed variable declaration debate. :)</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>Wow, that was a lot. C#9 added <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#record-types?wt.mc_id=DT-MVP-4024623" target="_blank">Record Types</a>, <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#init-only-setters?wt.mc_id=DT-MVP-4024623" target="_blank">Init Only</a> setters, <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#top-level-statements?wt.mc_id=DT-MVP-4024623" target="_blank">Top-Level</a> programs, enhancements to <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#pattern-matching-enhancements?wt.mc_id=DT-MVP-4024623" target="_blank">pattern matching</a>, and <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9?wt.mc_id=DT-MVP-4024623" target="_blank">more</a>.</p>

<p>I hope you take some time and play around with these new language features.  Doing so will reduce your keystrokes and help your code to be readable in the long run.</p>

<h2 id="bonus-coming-soon---c-10">Bonus: Coming Soon - C# 10</h2>

<p>While not set in stone… As of the writing of this post, .NET 6 preview 5 is planing on adding the following to C# 10.</p>

<ul>
  <li>Allow <code class="language-plaintext highlighter-rouge">const</code> interpolated strings.</li>
  <li>Record types can seal <code class="language-plaintext highlighter-rouge">ToString()</code>.</li>
  <li>Allow both assignment and declaration in the same deconstruction.</li>
  <li>Allow <code class="language-plaintext highlighter-rouge">AsyncMethodBuilder</code> attribute on methods.</li>
</ul>

<p>For more, check out <a href="https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10?wt.mc_id=DT-MVP-4024623">What’s new in C# 10.0</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term=".NET" /><category term="Record" /><category term="Init" /><summary type="html"><![CDATA[Note: This post was originally posted on the Telerik blog.]]></summary></entry><entry><title type="html">Software Architecture with C# 9 and .NET 5 - Book Review</title><link href="https://www.josephguadagno.net/2021/07/15/software-architecture-with-c-sharp-and-dot-net-5-book-review" rel="alternate" type="text/html" title="Software Architecture with C# 9 and .NET 5 - Book Review" /><published>2021-07-15T17:30:00-07:00</published><updated>2021-07-15T17:30:00-07:00</updated><id>https://www.josephguadagno.net/2021/07/15/software-architecture-with-c-sharp-and-dot-net-5-book-review</id><content type="html" xml:base="https://www.josephguadagno.net/2021/07/15/software-architecture-with-c-sharp-and-dot-net-5-book-review"><![CDATA[<p>The book’s title should be Software Architecture with Azure, C# 9, and .NET since the book focuses on the Microsoft stack and development tools.</p>

<p>The book starts with getting an understanding of what software architecture is.</p>

<ul>
  <li>Including software development processes</li>
  <li>The role of software architects</li>
  <li>Non Functional Requirements (NFRs).</li>
</ul>

<p>After introducing architecture, the author gets into designing and building software using Microsoft tools and services. Including how you can use Azure DevOps to document requirements, figuring out which Azure services to use to host and develop your application.</p>

<p>From there, you build an application from scratch, introducing architecture, design concepts, and other things to consider along the way. Finally, the last few chapters cover unit testing and functional testings, and building a CI/CD pipeline.</p>

<p>At the end of the book, you would have been exposed to ways to build highly scalable applications using Microsoft development tools and Microsoft Azure.</p>

<p>Overall, the other covers a lot of topics but scratches the surface with some of them.</p>

<p>On a side note, I like the Questions and Further Readings section that this book has.  It’s an excellent way to test if you picked up what the author wrote in the chapter. So add that with the Further Readings section to help the author drive home the point and provide the reader with additional reference materials.</p>

<p>Purchase <a href="https://amzn.to/3eoixmu" target="_blank">Software Architecture with C# 9 and .NET 5</a> on Amazon.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Book Reviews" /><category term="ASP.NET" /><category term="Architecture" /><category term="Azure" /><category term="ASP.NET Core MVC" /><category term=".NET" /><summary type="html"><![CDATA[The book’s title should be Software Architecture with Azure, C# 9, and .NET since the book focuses on the Microsoft stack and development tools.]]></summary></entry><entry><title type="html">7 Windows 11 Tips</title><link href="https://www.josephguadagno.net/2021/07/11/7-windows-11-tips" rel="alternate" type="text/html" title="7 Windows 11 Tips" /><published>2021-07-11T07:11:00-07:00</published><updated>2021-07-11T07:11:00-07:00</updated><id>https://www.josephguadagno.net/2021/07/11/7-windows-11-tips</id><content type="html" xml:base="https://www.josephguadagno.net/2021/07/11/7-windows-11-tips"><![CDATA[<p>It’s July 11th, or 7/11/21 (in the U.S.), so I am bringing you <strong>7</strong> Windows <strong>11</strong> tips. See what I did there. :smile: I know, it’s corny, and really only applicable in the U.S. since our calendar is different from the rest of world.  But you don’t need to store or display your date as 7/11 to use any one of these tips.</p>

<p class="notice--info"><strong><em>NOTE</em></strong>: These tips are based on an early access preview.  The images and functionality could change between releases including the final release. This was based on Windows 11 Pro, Update 21H2, Build 22000.65</p>

<h2 id="tip-1-move-the-taskbar">Tip 1: Move the Taskbar</h2>

<p>By default, the Windows Taskbar, is center aligned. However, you can change that in the settings.  To open the settings, press <code class="language-plaintext highlighter-rouge">Windows Key + S</code> to bring up the Windows Search, and in the text box type <code class="language-plaintext highlighter-rouge">settings</code>. The <em>Settings</em> application should look like this.</p>

<p><img src="/assets/images/posts/7-windows-11-tips-settings-application.png" alt="Settings Application" class="align-center" /></p>

<p>Once the <em>Settings</em> application is open, click on <em>Personalization</em>. Then expand <em>Taskbar behaviors</em>. <strong><em>NOTE</em></strong>, you may have to scroll for this.</p>

<p><img src="/assets/images/posts/7-windows-11-tips-taskbar-personalization.png" alt="Taskbar Personalization" class="align-center" /></p>

<p>Now, you can set the taskbar alignment to the left by changing the <code class="language-plaintext highlighter-rouge">Taskbar Alignment</code> setting.  No need to click a <em>Save</em> button, the saving is automatic.</p>

<h2 id="tip-2-start-menu">Tip 2: Start Menu</h2>

<p>The Start Menu had a few user interface changes.</p>

<p><img src="/assets/images/posts/windows11-devchannel-start-menu.png" alt="Windows 11 Start Menu" class="align-center" /></p>

<p>You can make some changes to it.  Sorry, you can’t bring back Live Tiles. :disappointed: However, you turn on and off the recent apps, most used apps, and recent apps settings.  Open up the <em>Settings</em> application, chose <em>Personalization</em>, then <em>Start</em>.</p>

<p><img src="/assets/images/posts/7-windows-11-tips-start-menu-personalization.png" alt="Start Menu Personalization" class="align-center" /></p>

<p>If you click <em>Folders</em>, you can customize the Start Menu by adding the Windows special folders next to the power button. You can add the following “Special Folders” to the Start Menu.</p>

<ul>
  <li>Settings</li>
  <li>File Explorer</li>
  <li>Documents</li>
  <li>Downloads</li>
  <li>Music</li>
  <li>Pictures</li>
  <li>Videos</li>
  <li>Network</li>
  <li>and, Personal folder</li>
</ul>

<p>I added, <em>Settings</em>, <em>File Explorer</em>, <em>Downloads</em>, and <em>Personal folder</em>. Now the bottom of my Start Menu looks like this.</p>

<p><img src="/assets/images/posts/7-windows-11-tips-start-menu-folders.png" alt="Start Menu Folders" class="align-center" /></p>

<h2 id="tip-3-time-estimates-for-windows-updates">Tip 3: Time Estimates for Windows Updates</h2>

<p>It’s late and you are about to call it a night, you look in your tray to see what time it is and notice the update icon in your task tray that indicates Windows has an update to apply.</p>

<p><img src="/assets/images/posts/7-windows-11-tips-update-to-apply.png" alt="Windows Update Icon" class="align-center" /></p>

<p>You start to think to yourself, <em>I wonder how long is this going to take?</em>.  Luckily you now no longer have to guess, Windows 11 guesses for you.  Click the power icon and you are prompted with estimates as to how long Windows should take to apply the updates and restart or shutdown.</p>

<p><img src="/assets/images/posts/windows11-devchannel-shutdown-estimate.png" alt="Windows 11 - Apply Update" class="align-center" /></p>

<p>Let’s just hope it never says ‘<em>5 seconds remaining</em>’ :smile:</p>

<h2 id="tip-4-search">Tip 4: Search</h2>

<p>The Search user interface has been revamped also. You can click the <code class="language-plaintext highlighter-rouge">Windows Key + S</code> or the magnifying glass on the Taskbar to open it.</p>

<p><img src="/assets/images/posts/windows11-devchannel-search.png" alt="Windows 11 - Windows Search" class="align-center" /></p>

<p>However, if you hover over the magnifying glass you will get a smaller window.</p>

<p><img src="/assets/images/posts/7-windows-11-tips-search-hover.png" alt="Windows 11 - Windows Search Hover" class="align-center" /></p>

<p>This window has a text box to enter your search terms and the most three recent applications or searches you performed.</p>

<h2 id="tip-5-snip--sketch">Tip 5: Snip &amp; Sketch</h2>

<p>Snip &amp; Sketch allows you to quickly annotate screenshots, photos, and other images with your pen, touch, or mouse and save, paste or share with other apps. It is included in Windows 11, or you can <a href="https://www.microsoft.com/en-us/p/snip-sketch/9mz95kl8mr0l?activetab=pivot:regionofsystemrequirementstab" target="_blank">download</a> it for Windows 10 or the Xbox One.</p>

<p>The keyboard shortcut of <code class="language-plaintext highlighter-rouge">Windows Key + Shift + S</code> will start a snipping session with Snip &amp; Sketch.</p>

<p><img src="/assets/images/posts/7-windows-11-snip-and-sketch.png" alt="Windows 11 - Snip &amp; Sketch" class="align-center" /></p>

<p>For more on Snip &amp; Sketch, check out <a href="https://www.elevenforum.com/t/take-and-annotate-screenshots-with-snip-sketch-app-in-windows-11.497/" target="_blank">Windows 11 Forum</a>.</p>

<h2 id="tip-6-quick-settings">Tip 6: Quick Settings</h2>

<p>The <em>Quick Settings</em> has been revamped. The <em>Quick Settings</em> can be accessed by clicking on the icons in the task tray, typically the battery, network, and sound icons are visible in the task tray.</p>

<p><img src="/assets/images/posts/windows11-devchannel-task-tray.png" alt="Windows 11 - Task Tray" class="align-center" /></p>

<p>Upon clicking the task tray you should see something like this</p>

<p><img src="/assets/images/posts/7-windows-11-tips-quick-settings.png" alt="Windows 11 - Quick Settings" class="align-center" /></p>

<p>Now clicking on the gear icon will bring up the <em>Settings</em> application.  Clicking on the <em>‘Pencil’</em> icon will allow you to add and remove buttons to the <em>Quick Settings</em> panel.</p>

<p><img src="/assets/images/posts/7-windows-11-tips-quick-settings-edit.png" alt="Windows 11 - Edit Quick Settings" class="align-center" /></p>

<p>From here, you can click the <em>pin</em> icons, noted by the arrow, to remove an item.  To add an item, click the <em>+ Add</em> button.  That brings up a menu similar to this.</p>

<p><img src="/assets/images/posts/7-windows-11-tips-quick-settings-add.png" alt="Windows 11 - Adding to Quick Settings" class="align-center" /></p>

<p><strong><em>NOTE</em></strong>: This menu can vary depending on your hardware and configuration.</p>

<h2 id="tip-7-clipboard-history">Tip 7: Clipboard History</h2>

<p>Technically not new to Windows but it has been drastically improved. Pressing <code class="language-plaintext highlighter-rouge">Windows Key + V</code> brings up the Clipboard History Window.</p>

<p><img src="/assets/images/posts/7-windows-11-tips-clipboard-history.png" alt="Windows 11 - Clipboard History" class="align-center" /></p>

<p>The Clipboard History window allows you to paste different types of data, emoji’s, GIFs, equations, contents of the clipboard, and not just the last item, and a lot more.</p>

<ol>
  <li>Recent Emoji’s and GIF search (powered by Tenor)</li>
  <li>Emoji’s</li>
  <li>GIFs (powered by Tenor)</li>
  <li><em>Classic</em> ASCII based emoticons</li>
  <li>Special characters, similar to that of CharMap</li>
  <li>The items that have been copied to the clipboard.</li>
</ol>

<p>If there are items that you want to keep on the clipboard, press the <em>pin</em> icon to save them.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>Every day I use Windows 11, I find something new about it. For more on Windows 11, you can check out my previous post, <a href="/2021/07/02/windows-11-a-first-look">Windows 11 - A first look</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Windows11" /><summary type="html"><![CDATA[It’s July 11th, or 7/11/21 (in the U.S.), so I am bringing you 7 Windows 11 tips. See what I did there. :smile: I know, it’s corny, and really only applicable in the U.S. since our calendar is different from the rest of world. But you don’t need to store or display your date as 7/11 to use any one of these tips.]]></summary></entry><entry><title type="html">Testing Web APIs or Services with JetBrains Rider</title><link href="https://www.josephguadagno.net/2021/07/05/testing-web-apis-or-services-with-jetbrains-rider" rel="alternate" type="text/html" title="Testing Web APIs or Services with JetBrains Rider" /><published>2021-07-05T21:20:00-07:00</published><updated>2021-07-05T21:20:00-07:00</updated><id>https://www.josephguadagno.net/2021/07/05/testing-web-apis-or-services-with-jetbrains-rider</id><content type="html" xml:base="https://www.josephguadagno.net/2021/07/05/testing-web-apis-or-services-with-jetbrains-rider"><![CDATA[<p>For the last week or so I’ve been working on modernizing the <a href="https://www.desertcodecamp.com" target="_blank">Desert Code Camp</a> site which meant that I am basically rewriting it. :smile:  Part of the rewrite is to make all of the user facing functionality, adding/viewing sessions, viewing the schedule, etc., available from an API and not code behind.  I spent the last week or so designing and implementing the API in ASP.NET Core 6.  The technology used in building the API does not matter for this post but the example repository is in ASP.NET Core. While testing the application, I started to heavily use the <strong>HTTP Client</strong> feature of <a href="https://www.jetbrains.com/rider/" target="_blank">JetBrains Rider</a>.  The <strong>HTTP Client</strong> is a bundled plugin, which is enabled by default, which allows you to emulate calls to a web service or web API.  It works very much like Postman, but in the IDE and included.</p>

<p>The <strong>HTTP Client</strong> supports GET, POST, and most HTTP verbs. It even has support for converting <a href="https://www.jetbrains.com/help/rider/Http_client_in__product__code_editor.html#converting-curl-requests" target="_blank">cURL</a> commands.</p>

<h2 id="getting-started">Getting Started</h2>

<p>I’ll be using the API that can be found GitHub at <a href="https://github.com/jguadagno/Contacts" target="_blank">https://github.com/jguadagno/Contacts</a> for the examples.</p>

<p>If you don’t have the plugin enabled, enable it. :smile: You can enable it by going into the <em>Settings</em> or <em>Preferences</em> in Rider and selecting <code class="language-plaintext highlighter-rouge">Plugins</code>.  You can also get there from <em>Navigate</em> then <em>Search Everything…</em> or <code class="language-plaintext highlighter-rouge">CTRL+T</code> and type <em>Plugins</em>. Once the plugin is enabled you can add an HTTP request file to your solution or as a <a href="https://www.jetbrains.com/help/rider/Scratches.html" target="_blank">scratch</a> file.  I tend to add them as part of the solution so any one working on the solution can use them.  Scratch files, for me, are more of a temporary file that I use for ‘one off’ requests.</p>

<p>In the Solution Explorer for Rider in one of the projects, you can right click and choose <em>Add…</em> then <em>HTTP Request</em>.</p>

<p><img src="/assets/images/posts/http-client-rider-add-new-http-request.png" alt="HTTP Client - Add new HTTP Request" class="align-center" /></p>

<p>This will add a new, blank, editor window for you to add the HTTP calls that you wish to make.</p>

<h3 id="http-request-editor">HTTP Request Editor</h3>

<p>The HTTP Request editor has a separate set of commands available to it.</p>

<p><img src="/assets/images/posts/http-client-rider-http-request-editor.png" alt="HTTP Client - Editor Commands" class="align-center" /></p>

<ul>
  <li><strong>Run all requests in file</strong> will execute every requests in this file.</li>
  <li><strong>Add request</strong> gives you a menu of starter requests.</li>
  <li><strong>Add environment file</strong> allows you to add either public or secret files to hold variables like URLs, user ids, passwords, etc. More on that later.</li>
  <li><strong>Convert…</strong> provides an interface for converting from cURL commands to HTTP Requests and a few others.</li>
  <li><strong>Open log</strong> opens the HTTP request log.</li>
  <li><strong>Examples</strong> contains a few starter examples to learn how to use the tool.</li>
</ul>

<h4 id="requests">Requests</h4>

<p>The HTTP request can contain a few “arguments”</p>

<ul>
  <li>Method</li>
  <li>Request-URI</li>
  <li>HTTP-Version (optional)</li>
  <li>Header-field and Header-value pairs</li>
  <li>Request-Body (optional)</li>
</ul>

<p>A request would look something like this.</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="go">Method Request-URI HTTP-Version
Header-field: Header-value

Request-Body
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="sample-get-request">Sample GET Request</h4>

<p><img src="/assets/images/posts/http-client-rider-sample-get.png" alt="HTTP Client - Sample Get Request" class="align-center" /></p>

<p>In this sample request, you will see Rider adds a play button (highlighted with the red triangle).</p>

<p>The request itself, line 1, is a GET request. Where you specify the verb and the URI to call.</p>

<p>Line 2 is a log file that was generated.  You can click on the file name to see the results. If you execute the request multiple times, you will see one file for each request.  Rider even provides you with the ability to compare the responses.  This is helpful for testing.</p>

<p>Line 3 is important! The <code class="language-plaintext highlighter-rouge">###</code> indicates that this ends the request.</p>

<h4 id="sample-post-request">Sample POST Request</h4>

<p>Since a Post request generally has data that goes along with it, the <strong>HTTP Request</strong> supports that.  Simply add the <code class="language-plaintext highlighter-rouge">Content-Type</code> and body in the lines after the <em>verb</em> <em>url</em> line.</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="go">POST https://localhost:5001/contacts
Content-Type: application/json

{
  "FirstName": "Joe",
  "LastName" : "Guadagno",
  "MiddleName" : "James",
  "EmailAddress" : "jguadagno@hotmail.com",
  "Phones": [
    {
      "phonenumber": "8675309",
      "extension": ""
    }
  ]
}
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="running-http-requests">Running HTTP Requests</h2>

<p>If the HTTP Request file has focus in the IDE, you can click on <em>Run all requests in file</em>, or the ‘play’ icon next to the verb.  If you are going to execute the requests multiple times, as I do when building APIs, you should <a href="https://www.jetbrains.com/help/rider/Http_client_in__product__code_editor.html#http-request-run-debug-configurations" target="_blank">create a <em>Run Configuration</em></a> for them.  Just be sure that your API is up and running before you make the HTTP request against it.  You can create a <em><a href="https://www.jetbrains.com/help/rider/Run_Debug_Configuration.html#create-compound-config" target="_blank">Compound Run Configuration</a></em> to start your API project and then run the HTTP requests.  Just note, at the time I wrote this post, only <strong>Run</strong> is supported and not <strong>Debug</strong>.</p>

<h3 id="environment-files">Environment Files</h3>

<p>Environment files allow you to define variables that are specific to your environment in your project.  Think of it like the <code class="language-plaintext highlighter-rouge">appSettings.json</code> in .NET but all in one file.</p>

<p>An example <code class="language-plaintext highlighter-rouge">http-client.env.json</code> file could look like this.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"dev"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"urlRoot"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://localhost:5001/"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"uat"</span><span class="p">:{</span><span class="w">
  </span><span class="nl">"urlRoot"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://uat.mydomain.com/"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"prod"</span><span class="p">:{</span><span class="w">
    </span><span class="nl">"urlRoot"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://mydomain.com/"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Each root object property becomes an ‘environment’ that you can select to run your HTTP requests in. In this example, you can select one of the three environments, <em>dev</em>, <em>uat</em>, or <em>prod</em>, when you run these request.  Now the variable <code class="language-plaintext highlighter-rouge">urlRoot</code>, can used now in all your requests and will be replaced with the value for the environment selected.</p>

<p>In the HTTP request file, change this</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="go">GET https://localhost:5001/contacts/37/phones
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>to this</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="go">GET /contacts/37/phones
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>and if you select <em>uat</em>, the HTTP request that gets run is.</p>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="go">GET https://uat.mydomain.com/contacts/37/phones
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>This allows you to control the url, and other variables without having to edit the file. Win!</p>

<p>Rider supports two types of environment files, regular and private.</p>

<h4 id="regular-environment-file">Regular Environment File</h4>

<p>Choosing a <strong>Regular</strong> file will create the <code class="language-plaintext highlighter-rouge">http-client.env.json</code> file. This file can contain common variables such as host name, port, or query parameters, and is meant to be distributed together with your project.</p>

<h4 id="private-environment-files">Private Environment Files</h4>

<p>Choosing <strong>Private</strong> will create the <code class="language-plaintext highlighter-rouge">http-client.private.env.json</code> file. This file might include passwords, tokens, certificates, and other sensitive information. It is added to the list of source code system ignored files by default. <strong><em>NOTE</em></strong>: The values of variables that are specified in the <code class="language-plaintext highlighter-rouge">http-client.private.env.json</code> file override the values in the regular environment file.</p>

<p>There is a lot more to environment files and variables.  You can read more <a href="https://www.jetbrains.com/help/rider/Exploring_HTTP_Syntax.html#using_request_vars" target="_blank">here</a>.</p>

<h3 id="more-samples">More Samples</h3>

<p>You can find more samples in the <a href="https://github.com/jguadagno/Contacts/blob/main/src/Contacts-Sample-Requests.http" target="_blank">Contacts-Sample-Requests.http</a> sample HTTP request file that I used for the <a href="https://github.com/jguadagno/Contacts" target="_blank">Contacts</a> sample application and API.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>This was a quick introduction to using the <strong>HTTP Client</strong> in JetBrains Rider to help testing an API or web service. It’s helped me a lot.  Hopefully it will be equally as helpful to you!</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://www.jetbrains.com/help/rider/Testing_RESTful_Web_Services.html" target="_blank">Test RESTful Web services</a></li>
  <li><a href="https://www.jetbrains.com/help/rider/Http_client_in__product__code_editor.html" target="_blank">HTTP client in JetBrains Rider code editor</a></li>
  <li><a href="https://www.jetbrains.com/help/rider/Exploring_HTTP_Syntax.html" target="_blank">Exploring the HTTP request in the Editor syntax</a></li>
  <li><a href="https://www.jetbrains.com/help/rider/HTTP_Response_handling_API_reference.html" target="_blank">HTTP Response handling API reference</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="JetBrains" /><category term="Rider" /><category term="WebAPI" /><category term="ASP.NET Core" /><summary type="html"><![CDATA[For the last week or so I’ve been working on modernizing the Desert Code Camp site which meant that I am basically rewriting it. :smile: Part of the rewrite is to make all of the user facing functionality, adding/viewing sessions, viewing the schedule, etc., available from an API and not code behind. I spent the last week or so designing and implementing the API in ASP.NET Core 6. The technology used in building the API does not matter for this post but the example repository is in ASP.NET Core. While testing the application, I started to heavily use the HTTP Client feature of JetBrains Rider. The HTTP Client is a bundled plugin, which is enabled by default, which allows you to emulate calls to a web service or web API. It works very much like Postman, but in the IDE and included.]]></summary></entry><entry><title type="html">Windows 11 - A First Look</title><link href="https://www.josephguadagno.net/2021/07/02/windows-11-a-first-look" rel="alternate" type="text/html" title="Windows 11 - A First Look" /><published>2021-07-02T21:35:00-07:00</published><updated>2021-07-05T16:30:00-07:00</updated><id>https://www.josephguadagno.net/2021/07/02/windows-11-a-first-look</id><content type="html" xml:base="https://www.josephguadagno.net/2021/07/02/windows-11-a-first-look"><![CDATA[<p>Windows 11 or I am calling it right now, the <em>Who Moved My Cheese Edition</em>, was announced and shown to the world a few days ago. Why do you call it the <em>Who Moved My Cheese Edition</em>, you ask? Not so much since things were moved around but more so the User Interface has changed a lot. As Microsoft puts it, <em>A Fresh Perspective</em>.</p>

<blockquote>
  <p>Windows 11 provides a calm and creative space where you can pursue your passions through a fresh experience. From a rejuvenated Start menu to new ways to connect to your favorite people, news, games, and content—Windows 11 is the place to think, express, and create in a natural way.</p>
</blockquote>

<p>I was curious as to what the new Windows 11 was going to be like. So I wanted to get it installed.</p>

<p class="notice--info">At the writing of this posts, Windows 11 is only available to <a href="https://insider.windows.com/" target="_blank">Windows Insiders</a> in the <strong>Dev</strong> channel.</p>

<p>I’m a Windows insider but in the <em>Beta</em>. So on Thursday, I switched to the <strong>Dev</strong> channel and Windows 11 almost immediately started downloading after I verified the laptop I was planning to install Windows 11 on meet the <a href="https://www.microsoft.com/en-us/windows/windows-11-specifications" target="_blank">minimum hardware requirements</a>.</p>

<p><img src="/assets/images/posts/windows11-devchannel-restart.png" alt="Windows 11 Downloading" class="align-center" /></p>

<p>The download completed in under 30 minutes.  Once I clicked restart, after about 10 minutes and three or four reboots I was presented with a shiny new Windows 11.</p>

<p>Here is what I noticed in the first few hours of using Windows 11.</p>

<p class="notice--info"><strong><em>NOTE</em></strong>: Last updated on July 5th, 2021 with the Update/Shutdown estimates.</p>

<h2 id="user-interface-changes">User Interface Changes</h2>

<p>From what I can tell a lot of work went into creating a new, modern user interface.  The font selection is different, more crisp.  There icons have more color. Windows now have curved corners instead of the rectangular corners.</p>

<h2 id="operating-systems-updates">Operating Systems Updates</h2>

<p>Now when the system has updates to apply and you chose the power icon, you get prompted with estimates as to how long Windows should take to apply the updates and restart or shutdown.</p>

<p><img src="/assets/images/posts/windows11-devchannel-shutdown-estimate.png" alt="Windows 11 - Apply Update" class="align-center" /></p>

<p>Let’s just hope it never says ‘<em>5 seconds remaining</em>’ :smile:</p>

<h3 id="start-menu">Start Menu</h3>

<p>The Start menu is one of the biggest and most notable changes.  It’s been redone, again :smile:.  There are no more Live Tiles but static icons and text.</p>

<p><img src="/assets/images/posts/windows11-devchannel-start-menu.png" alt="Windows 11 - Start Menu" class="align-center" /></p>

<p>All of the <em>pinned</em> applications are on the top.  This is a scrolling list as indicated by the two dots on the right hand side.  The lower half of the Start menu are your <em>recommended</em> applications and files. In the image above I have the <em>Get Started</em>, a few PowerPoint decks, a Word document, and an Excel spreadsheet. You can click on the <em>All apps &gt;</em> button on the top to get an alphabetical list of applications installed. Clicking on the <em>More &gt;</em> will take you to a list of your recent files.</p>

<h3 id="task-bar">Task Bar</h3>

<p>The task bar is probably the first notable change you will see.</p>

<p><img src="/assets/images/posts/windows11-devchannel-taskbar-icons.png" alt="Windows 11 - Task Bar" class="align-center" /></p>

<p>If you are a MacOS user, you will notice some similarity.  The new task bar is centered on your desktop.  It may be possible to move it but I haven’t checked yet.  There are dashes or underlines underneath the icons to let you know which ones are open and which is the window with focus.  In the image above, I have Microsoft Edge, Slack, and Windows File Explorer open.  Windows File Explorer was the active window at the time of the snapshot.</p>

<p>There are also some new buttons added to the left of the task bar.</p>

<p><img src="/assets/images/posts/windows11-devchannel-taskbar-special-icons.png" alt="Windows 11 - Task Bar - Special Icons" class="align-center" /></p>

<p>The first icon is the Windows Start Menu, highlighted above.</p>

<p>The second icon is the new Windows Search.</p>

<p><img src="/assets/images/posts/windows11-devchannel-search.png" alt="Windows 11 - Windows Search" class="align-center" /></p>

<p>Not much different with this search page from previous versions.</p>

<p>The third button is the task view. Which if you hover over it brings up a smaller version of what is running on your desktops.</p>

<p><img src="/assets/images/posts/windows11-devchannel-task-view.png" alt="Windows 11 - Task View - Preview" class="align-center" /></p>

<p>If you click on the Task View button, or four finger swipe upwards, you’ll get a new task view.  So far, I am not a fan, it currently added and extra title bar to the windows.  This might be to enable the <em>touch</em> experience with tablets.</p>

<p><img src="/assets/images/posts/windows11-devchannel-app-view.png" alt="Windows 11 - Task View" class="align-center" /></p>

<p>The fourth button is the new Widget component.  More on that later.</p>

<h4 id="task-tray">Task Tray</h4>

<p>The next round of noticeable changes were in the task tray.  The task tray is that area, typically in the lower right hand corner of the screen where the date and time are displayed.</p>

<p><img src="/assets/images/posts/windows11-devchannel-task-tray.png" alt="Windows 11 - Task Tray" class="align-center" /></p>

<h4 id="notification-window">Notification Window</h4>

<p>The notification window had some big changes. This is brought up if you click on the network/sound icons.</p>

<p><img src="/assets/images/posts/windows11-devchannel-notification-center.png" alt="Windows 11 - Notification Center" class="align-center" /></p>

<p>Here you will the first set of changes which are more touch friendly.  There is more spacing around the buttons and sliders. In addition, there is easy access to the configuration of the notifications and settings.</p>

<h2 id="windows-file-explorer">Windows File Explorer</h2>

<p>The Windows File Explorer received some new icons and better spacing between objects.</p>

<h3 id="the-navigation-pane">The Navigation Pane</h3>

<p><img src="/assets/images/posts/windows11-devchannel-navigation-pane.png" alt="Windows 11 - Explorer - Navigation Pane" class="align-center" /></p>

<p>You might notice the <em>Linux</em> folder item.  This appears because I have Docker on this machine along with <a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10?wt.mc_id=DT-MVP-4024623">Windows Subsystem for Linux (WSL)</a></p>

<p><img src="/assets/images/posts/windows11-devchannel-linux.png" alt="Windows 11 - Explorer - Linux" class="align-center" /></p>

<h3 id="folder-icons">Folder Icons</h3>

<p>The folder icons received some updates also.</p>

<p><img src="/assets/images/posts/windows11-devchannel-folder-icons.png" alt="Windows 11 - Explorer - Folder Icons" class="align-center" /></p>

<h2 id="new-functionality">New Functionality</h2>

<p>These are the new(ish) items I discovered in the Windows 11</p>

<h3 id="clipboard-viewer-and-then-some">Clipboard Viewer and then some</h3>

<p>What used to the be the Clipboard Viewer (Accessible via the Windows Key + V) not only views the clipboard but allows you to paste, emoticons, GIFs, special characters, and more.  It’s almost like its an extra input manager.</p>

<p><img src="/assets/images/posts/windows11-devchannel-clipboard-viewer.png" alt="Windows 11 - Clipboard Viewer" class="align-center" /></p>

<h3 id="widgets">Widgets</h3>

<p>This is the biggest, net new, functionality that I’ve seen.  Widgets seems like the first step in supporting running Android applications and mimicking some of its functionality.</p>

<p><img src="/assets/images/posts/windows11-devchannel-widget-display.png" alt="Windows 11 - Widgets" class="align-center" /></p>

<p>In this very long screenshot you will see there are a lot of Widgets on by default, I will be disabling most of them :smile:</p>

<p>You can add/remove widgets by clicking the <strong>Add Widget</strong> button.  Here are the options that we presented at the time of writing this post.</p>

<p><img src="/assets/images/posts/windows11-devchannel-add-widgets.png" alt="Windows 11 - Add/Remove Widgets" class="align-center" /></p>

<h3 id="teams-integration">Teams Integration</h3>

<p>Having tried it yet, maybe an future update to this post will have something. According to the Windows 11 site.</p>

<blockquote>
  <p>With Windows 11, we’re excited to introduce Chat from Microsoft Teams integrated in the taskbar. Now you can instantly connect through text, chat, voice or video with all of your personal contacts, anywhere, no matter the platform or device they’re on, across Windows, Android or iOS. If the person you’re connecting to on the other end hasn’t downloaded the Teams app, you can still connect with them via two-way SMS.</p>
</blockquote>

<h3 id="android-applications">Android Applications</h3>

<p>While this feature is not yet available, the plan is to all you to be able to discover Android application through the Windows store and download them through the Amazon App Store.  And yes, <em>they will run on the Windows PC</em>.  This is probably why we see more of the Linux/WSL integration.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>I’ll update this post as I experiment more with Windows 11 over the next few days.</p>

<p class="notice--info">Please remember that this is an early access version, some of the features maybe removed or changed.</p>

<p>More on <a href="https://www.microsoft.com/en-us/windows/windows-11">Windows 11</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term="Windows11" /><summary type="html"><![CDATA[Windows 11 or I am calling it right now, the Who Moved My Cheese Edition, was announced and shown to the world a few days ago. Why do you call it the Who Moved My Cheese Edition, you ask? Not so much since things were moved around but more so the User Interface has changed a lot. As Microsoft puts it, A Fresh Perspective.]]></summary></entry><entry><title type="html">Migrate to ASP.NET Core MVC from ASP.NET Framework MVC</title><link href="https://www.josephguadagno.net/2021/05/19/migrate-to-asp-net-core-mvc-from-asp-net-mvc-framework" rel="alternate" type="text/html" title="Migrate to ASP.NET Core MVC from ASP.NET Framework MVC" /><published>2021-05-19T06:00:00-07:00</published><updated>2021-05-19T06:00:00-07:00</updated><id>https://www.josephguadagno.net/2021/05/19/migrate-to-asp-net-core-mvc-from-asp-net-mvc-framework</id><content type="html" xml:base="https://www.josephguadagno.net/2021/05/19/migrate-to-asp-net-core-mvc-from-asp-net-mvc-framework"><![CDATA[<p>.NET has been around for a while and has had many versions. We started the .NET Framework, moved into .NET Core, and now just .NET, kind of sort of :smile:. Keeping your application up with the latest version of .NET can be challenging. There are budgets to consider, time to learn the differences, and time to “fit in the migration.” Migrating or “upgrading” an ASP.NET MVC from .NET Framework to ASP.NET Core can be easy at times. Other times it can be just hard. There are no silver bullets to the migration as each project and solution is different. I’m going to walk you through one of the ways you can have a successful migration from ASP.NET MVC to ASP.NET Core MVC. At the end of the post, I’ll show you some gotchas that I have run across migrating applications.</p>

<h2 id="getting-started">Getting Started</h2>

<p>For this post, I will assume that we are working on an ASP.NET MVC (.NET Framework) that is a single project solution, which means that the data access, business logic, models, etc., are all in one solution. Similar to this.</p>

<p><img src="/assets/images/posts/migrate-to-core-solution-explorer.png" alt="Sample Solution Explorer" class="align-center" /></p>

<p>This single project is an ASP.NET MVC application written with .NET Framework 4.5.2-4.8. There is a SQL Server data dependency where the database is present in the <em>App_Data</em> folder. The data access is handled through EntityFramework. You can find a completed project <a href="https://github.com/jguadagno/sample-app" target="_blank">repository</a> as well as the database <a href="https://github.com/jguadagno/sample-app/blob/master/sql/readme.md" target="_blank">setup</a> instructions on GitHub.</p>

<p>Microsoft has made it easy to build a self-contained application and combine the user interface with the database and any business logic you need. However, with the application tightly coupled, this style makes it challenging to migrate or upgrade or even test your application. Our approach will be to break up the application into different layers or responsibilities, like the user interface, data layer/repository, and business/service layer.</p>

<p>While there is the <a href="https://dotnet.microsoft.com/platform/upgrade-assistant" target="_blank">.NET Upgrade Assistant</a> to help you, its still in preview and only does some of the leg work for you.  Dave Brock put together a nice <a href="https://www.daveabrock.com/2021/04/18/meet-dotnet-upgrade-assistant-your-dotnet-5-moving-company/" target="_blank">post</a> on working with it. I’ll walk you through some of the steps to redesign your application to make it a bit easier for this update and any more updates. Hopefully, that does not happen. :smile</p>

<h2 id="separate-the-models">Separate the models</h2>

<p>Putting your domain or data transfer objects into a separate project is the first step in the migration. Having your domain objects, like Customer, Order, etc., in a separate library allows you to start breaking your application into layers. This domain layer with all of the models that describe your data/objects will be used throughout the new solution to communicate data between the layers.</p>

<p>Assuming you are using Entity Framework to access your database along with the <em>code-based</em> model development and not the <em>EDMX-based</em>.</p>

<p class="notice--info">If your application uses the <em>EDMX-based</em> approach, follow the <a href="https://docs.microsoft.com/en-us/ef/efcore-and-ef6/porting/port-edmx?wt.mc_id=DT-MVP-4024623" target="_blank">Porting an EF6 EDMX-Based Model to EF Core</a> guide to update to <em>code-based</em> model approach.  Going forward with EntityFramework Core, the <em>EDMX-based</em> models are not being used.</p>

<p>The first thing you’ll want to do is create a new class library targeting .NET Standard. Why .NET Standard and not just .NET? Having the shared libraries like the Domain or Data libraries in .NET Standard allows you greater portability between projects and platforms. This approach will also allow you to slowly migrate pieces of the main project while keeping it up.
Now move those model classes over to the new project. I would name it something like <code class="language-plaintext highlighter-rouge">Contacts.Domain</code>. I typically put all of the models in a <code class="language-plaintext highlighter-rouge">Models</code> folder.</p>

<p><img src="/assets/images/posts/migrate-to-core-contacts-model.png" alt="Contact Models" class="align-center" /></p>

<p>You’ll want to add a reference to the new <code class="language-plaintext highlighter-rouge">Contacts.Domain</code> library to the existing Contacts application.  Don’t forget to update the using statements!</p>

<p class="notice--info"><strong><em>Note</em></strong>: When moving classes/files in between folders, namespaces, or projects, use the <em>Move Instance Method</em> refactoring (<a href="https://docs.microsoft.com/en-us/visualstudio/ide/reference/move-type-to-namespace?view=vs-2019&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Visual Studio</a> or <a href="https://www.jetbrains.com/help/resharper/Refactorings__Move__Instance_Method_to_Another_Type.html" target="_blank">JetBrains Rider/Resharper</a>)</p>

<h2 id="separate-the-data-layer">Separate the data layer</h2>

<p>Now let’s work on getting data access methods out of the user interface (web app). First, we’ll want to create a new class library targeting .NET Standard and add a reference to EntityFrameworkCore. The next part can be challenging, depending on how you have your application set up.</p>

<p>I am assuming that most of the data access for your application in the controller methods looks like this.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">ActionResults</span> <span class="nf">Index</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">var</span> <span class="n">_db</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Contact</span><span class="p">.</span><span class="nf">ContactsContext</span><span class="p">();</span>

    <span class="kt">var</span> <span class="n">contacts</span> <span class="p">=</span> <span class="n">_db</span><span class="p">.</span><span class="n">Contacts</span><span class="p">.</span><span class="nf">ToList</span><span class="p">();</span>

    <span class="k">return</span> <span class="nf">View</span><span class="p">(</span><span class="n">contacts</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>or</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">ActionResults</span> <span class="nf">Index</span><span class="p">()</span> <span class="p">{</span>
 
    <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">_db</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Contact</span><span class="p">.</span><span class="nf">ContactsContext</span><span class="p">())</span> 
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">contacts</span> <span class="p">=</span> <span class="n">_db</span><span class="p">.</span><span class="n">Contacts</span><span class="p">.</span><span class="nf">ToList</span><span class="p">();</span>

        <span class="k">return</span> <span class="nf">View</span><span class="p">(</span><span class="n">contacts</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now how you build up the data layer is up to you. I typically follow the manager or repository pattern. There are a lot of design patterns that you can follow. The choice is yours and not the intent of this blog post. The goal is to have one or more classes responsible for handling the saving, updating, deleting, and querying the data for the user interface.</p>

<p>Create the EntityFramework <a href="https://docs.microsoft.com/en-us/ef/ef6/fundamentals/working-with-dbcontext?wt.mc_id=DT-MVP-4024623" target="_blank">database context</a></p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="rouge-code"><pre><span class="k">namespace</span> <span class="nn">Contacts.Data</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ContactContext</span> <span class="p">:</span> <span class="n">DbContext</span>
    <span class="p">{</span>
        <span class="k">private</span> <span class="k">readonly</span> <span class="n">IConfiguration</span> <span class="n">_configuration</span><span class="p">;</span>
        <span class="k">public</span> <span class="nf">ContactContext</span><span class="p">(</span><span class="n">IConfiguration</span> <span class="n">configuration</span><span class="p">)</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="p">}</span>
        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;</span> <span class="n">Contacts</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>
        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Address</span><span class="p">&gt;</span> <span class="n">Addresses</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>
        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">Phone</span><span class="p">&gt;</span> <span class="n">Phones</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>
        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">AddressType</span><span class="p">&gt;</span> <span class="n">AddressTypes</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>
        <span class="k">public</span> <span class="n">DbSet</span><span class="p">&lt;</span><span class="n">PhoneType</span><span class="p">&gt;</span> <span class="n">PhoneTypes</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>

        <span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnConfiguring</span><span class="p">(</span><span class="n">DbContextOptionsBuilder</span> <span class="n">options</span><span class="p">)</span>
            <span class="p">=&gt;</span> <span class="n">options</span><span class="p">.</span><span class="nf">UseSqlServer</span><span class="p">(</span><span class="n">_configuration</span><span class="p">.</span><span class="nf">GetConnectionString</span><span class="p">(</span><span class="s">"ContactsDatabaseSqlServer"</span><span class="p">));</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here is a sample of what the <code class="language-plaintext highlighter-rouge">ContactRepository</code> class could look like.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre><span class="k">namespace</span> <span class="nn">Contacts.Data</span> 
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">ContactRepository</span> 
    <span class="p">{</span>

        <span class="k">public</span> <span class="n">Domain</span><span class="p">.</span><span class="n">Contact</span> <span class="nf">GetContact</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">)</span> 
        <span class="p">{</span>
            <span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">_db</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Contacts</span><span class="p">.</span><span class="n">Data</span><span class="p">.</span><span class="nf">ContactContext</span><span class="p">())</span>
            <span class="p">{</span>
                <span class="k">return</span> <span class="n">_db</span><span class="p">.</span><span class="n">Contacts</span><span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">c</span> <span class="p">=&gt;</span> <span class="n">c</span><span class="p">.</span><span class="n">ContactId</span> <span class="p">==</span> <span class="n">contactId</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
        <span class="c1">/// rest of the class removed for brevity</span>
    <span class="p">}</span>
<span class="p">}</span>

</pre></td></tr></tbody></table></code></pre></div></div>

<p>Once you moved all of the data access from the previous user interface to the new data project, you should be able to replace your database calls with Data.<em>method name</em>, like <code class="language-plaintext highlighter-rouge">Contacts.Data.GetContact(contactId)</code> using the above sample.</p>

<p>This approach may seem a bit risky or scary since you keep replacing portions of your application. I’d be lying if I said it wasn’t risky and scary. The truth is, it is risky and scary. However, you can mitigate some of the risks and make it easier to make changes in the future. Have I piqued your interest yet? That is where unit tests come in. But before we can build our unit tests, we will need to do some work on our solution to enable the mocking of our data repository classes. No, not <a href="https://www.dictionary.com/browse/mocking" target="_blank">mock</a> them, but <a href="https://www.telerik.com/products/mocking/unit-testing.aspx" target="_blank">mock</a> them :smile:.  Mocking complements unit testing frameworks by isolating dependencies through creating replacement objects. In our example, we will be mocking or “faking” our database calls.</p>

<p>To mock our repository, we will need to create an interface for the repository so most mocking frameworks can build the objects for it.</p>

<p class="notice--info"><strong><em>Note</em></strong>: If you are using a commercial testing/mocking framework like <a href="https://www.telerik.com/products/mocking.aspx" target="_blank">Telerik JustMock</a>, you do not need to create the interface.  It just works.  They even have support for <a href="https://www.telerik.com/products/mocking/mock-entity-framework.aspx" target="_blank">mocking EntityFramework classes</a>.</p>

<p>Creating the Interface for the newly created Data library can be done in two ways, manually or automatically.  I recommend the automatic way which involves selecting the class name, clicking and choosing ‘Refactor’ | ‘Extract Interface’. Be sure to put the interfaces in the same class library as the models.</p>

<p>The interface will look something like this.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="k">namespace</span> <span class="nn">Contacts.Domain.Interfaces</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">interface</span> <span class="nc">IContactRepository</span>
    <span class="p">{</span>
        <span class="n">Contact</span> <span class="nf">GetContact</span><span class="p">(</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">);</span>
        <span class="c1">/// other methods removed for brevity</span>
    <span class="p">}</span>
<span class="p">}</span>  
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="building-a-unit-test-suite">Building a Unit Test Suite</h2>

<p>I do not intend this section to be a thorough walk-through of unit tests. I will not cover every possible scenario that you should or should not cover. The amount of unit test and the complexity of them is more of an art than a science. When building unit tests, I try to cover the <a href="https://searchsoftwarequality.techtarget.com/definition/happy-path-testing" target="_blank">happy path</a>, the exception path, and the <a href="https://cucumber.io/blog/test-automation/happy-unhappy-paths-why-you-need-to-test-both/" target="_blank">unhappy path</a>. Does it work like it’s supposed to? Do I handle known and common exceptions? Do I handle none/common bad data entry? But again, your mileage may vary.</p>

<p>Here is a sample of the <code class="language-plaintext highlighter-rouge">GetContact</code> unit tests</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="n">Fact</span><span class="p">]</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">GetContact_WithAnInvalidId_ShouldReturnNull</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Arrange </span>
    <span class="kt">var</span> <span class="n">mockContactRepository</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">IContactRepository</span><span class="p">&gt;();</span>
    <span class="n">mockContactRepository</span><span class="p">.</span><span class="nf">Setup</span><span class="p">(</span><span class="n">contactRepository</span> <span class="p">=&gt;</span>
        <span class="n">contactRepository</span><span class="p">.</span><span class="nf">GetContact</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="nf">IsInRange</span><span class="p">(</span><span class="kt">int</span><span class="p">.</span><span class="n">MinValue</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">Range</span><span class="p">.</span><span class="n">Inclusive</span><span class="p">))</span>
    <span class="p">).</span><span class="n">Returns</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;(</span><span class="k">null</span><span class="p">);</span>

    <span class="kt">var</span> <span class="n">contactManager</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ContactManager</span><span class="p">(</span><span class="n">mockContactRepository</span><span class="p">.</span><span class="n">Object</span><span class="p">);</span>

    <span class="c1">// Act</span>
    <span class="kt">var</span> <span class="n">contact</span> <span class="p">=</span> <span class="n">contactManager</span><span class="p">.</span><span class="nf">GetContact</span><span class="p">(-</span><span class="m">1</span><span class="p">);</span> <span class="c1">// Any number less than zero</span>

    <span class="c1">// Assert</span>
    <span class="n">Assert</span><span class="p">.</span><span class="nf">Null</span><span class="p">(</span><span class="n">contact</span><span class="p">);</span>
<span class="p">}</span>

<span class="p">[</span><span class="n">Fact</span><span class="p">]</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">GetContact_WithAValidId_ShouldReturnContact</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Arrange </span>
    <span class="kt">var</span> <span class="n">mockContactRepository</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Mock</span><span class="p">&lt;</span><span class="n">IContactRepository</span><span class="p">&gt;();</span>
    <span class="n">mockContactRepository</span><span class="p">.</span><span class="nf">Setup</span><span class="p">(</span><span class="n">contactRepository</span> <span class="p">=&gt;</span>
        <span class="n">contactRepository</span><span class="p">.</span><span class="nf">GetContact</span><span class="p">(</span><span class="n">It</span><span class="p">.</span><span class="nf">IsInRange</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="kt">int</span><span class="p">.</span><span class="n">MaxValue</span><span class="p">,</span> <span class="n">Range</span><span class="p">.</span><span class="n">Inclusive</span><span class="p">))</span>
    <span class="p">).</span><span class="nf">Returns</span><span class="p">((</span><span class="kt">int</span> <span class="n">contactId</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="n">Contact</span>
    <span class="p">{</span>
        <span class="n">ContactId</span> <span class="p">=</span> <span class="n">contactId</span>
    <span class="p">});</span>

    <span class="kt">var</span> <span class="n">contactManager</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ContactManager</span><span class="p">(</span><span class="n">mockContactRepository</span><span class="p">.</span><span class="n">Object</span><span class="p">);</span>
    <span class="k">const</span> <span class="kt">int</span> <span class="n">requestedContactId</span> <span class="p">=</span> <span class="m">1</span><span class="p">;</span>

    <span class="c1">// Act</span>
    <span class="c1">// Assumes that a contact record exists with the ContactId of 1</span>
    <span class="kt">var</span> <span class="n">contact</span> <span class="p">=</span> <span class="n">contactManager</span><span class="p">.</span><span class="nf">GetContact</span><span class="p">(</span><span class="n">requestedContactId</span><span class="p">);</span>

    <span class="c1">// Assert</span>
    <span class="n">Assert</span><span class="p">.</span><span class="nf">NotNull</span><span class="p">(</span><span class="n">contact</span><span class="p">);</span>
    <span class="n">Assert</span><span class="p">.</span><span class="nf">Equal</span><span class="p">(</span><span class="n">requestedContactId</span><span class="p">,</span> <span class="n">contact</span><span class="p">.</span><span class="n">ContactId</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="create-a-new-web-application">Create a New Web Application</h2>

<p>Yes, I said it, create a new Web Application. However, it’s not going to be as hard as it may seem. We will create the new project using the template so that most of the new “plumbing code” gets creating for us. I’ll walk through the parts that are different. Since we are assuming your application was written using ASP.NET MVC, be sure to create a new Project and chose ASP.NET Core Web Application along with the “Model View Controller” type.</p>

<p><strong><em>Tip</em></strong>, while you are creating a new Web Application, you can use the application templates that are part of the <a href="https://bit.ly/3yjZ9zo" target="_blank">Telerik UI for ASP Core Component Suite</a> of components and controls to make your development a lot easier and faster.</p>

<h3 id="new-web-application---rider">New Web Application - Rider</h3>

<p><img src="/assets/images/posts/migrate-to-core-new-ui-rider.png" alt="Create new Web Application - Rider" class="align-center" /></p>

<h3 id="new-web-application---visual-studio">New Web Application - Visual Studio</h3>

<p><img src="/assets/images/posts/migrate-to-core-new-ui-visual-studio.png" alt="Create new Web Application - Visual Studio" class="align-center" /></p>

<h3 id="whats-different">What’s different</h3>

<p>Let’s look at the folder structure and new files.</p>

<p><img src="/assets/images/posts/migrate-to-core-new-files.png" alt="New files and folders" class="align-center" /></p>

<h4 id="folders">Folders</h4>

<p>The first couple of folders for this sample are the same: Dependencies, Properties, Models, Services, and Views. I’ve copied the models, views, and services from my previous project.  You’ll notice that one folder is missing <em>Content</em>.  That’s because the files in <em>Content</em>, more so the static files, have been moved to the new <em>wwwroot</em> folder.  Here you find folders for <em>css</em>, <em>js</em>, <em>lib</em>, and <em>favicon.ico</em>.  The idea is stuff that doesn’t change and is not part of the ASP.NET generated pages or logic gets placed in the <em>wwwroot</em> folder. The content in the <em>wwwroot</em> folder is served up with respect to the root of the application.  So if my application was <a href="https://www.josephguadagno.net" target="_blank">https://www.josephguadagno.net</a>, anything in the <code class="language-plaintext highlighter-rouge">wwwroot</code> would be served from <a href="https://www.josephguadagno.net" target="_blank">https://www.josephguadagno.net</a>. The <code class="language-plaintext highlighter-rouge">favicon.ico</code> would be served at <a href="https://www.josephguadagno.net/favicon.ico" target="_blank">https://www.josephguadagno.net/favicon.ico</a>. So you can move your images in this folder.  Just remember if you move your images to create some rewriting rules or mirror the path you originally had them in.</p>

<h4 id="files">Files</h4>

<p>Some files are gone, and some files are new. Missing are the <em>web.*.config</em>, <em>package.config</em>, and <em>global.asax</em>. The <em>web.*.config</em> was replaced by the <em>appSetting.json</em>, more on that later. The <em>package.config</em> was moved to “inside” the csproj file. The <em>global.asax</em> was mostly replaced by the <em>Startup.cs</em> file. There are some new files also: <em>appsettings.*.json</em>, <em>Program.cs</em>, and <em>Startup.cs</em>.</p>

<h5 id="application-configuration">Application Configuration</h5>

<p>Goodbye <em>web.config</em>! It was fun, but you were messy and hard to deal with at times. Hello <em>appsettings.json</em>.  The <em>appsettings.json</em> is the application configuration model for .NET and ASP.NET Core.</p>

<p>A “typical” starter application configuration would look something like this.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="p">{</span>
  <span class="dl">"</span><span class="s2">ConnectionStrings</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">ContactsDatabaseSqlServer</span><span class="dl">"</span><span class="p">:</span> <span class="dl">""</span>
  <span class="p">},</span>
  <span class="dl">"</span><span class="s2">Logging</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">LogLevel</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
      <span class="dl">"</span><span class="s2">Default</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Information</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">Microsoft</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Warning</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">Microsoft.Hosting.Lifetime</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Information</span><span class="dl">"</span>
    <span class="p">}</span>
  <span class="p">},</span>
  <span class="dl">"</span><span class="s2">AllowedHosts</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">*</span><span class="dl">"</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here we are defining the connection string of <code class="language-plaintext highlighter-rouge">ContactsDatabaseSqlServer</code> in the <code class="language-plaintext highlighter-rouge">ConnectionStrings</code> object and defining the logging for the application.</p>

<p>You’ll notice that, by default, there is an <em>appsettings.json</em> and an <em>appsettings.Development.json</em>. ASP.NET Core supports configuration by environment. There is no longer a need to have to deal with the web.config transformations. In the <em>appsettings.Development.json</em> file, just add whatever setting you want to <em>override</em> for development.  In this sample, I would want to update my database connection in development. The <em>appsettings.Development.json</em> would look like this.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="p">{</span>
  <span class="dl">"</span><span class="s2">ConnectionStrings</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">ContactsDatabaseSqlServer</span><span class="dl">"</span><span class="p">:</span> <span class="dl">""</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>For more on the configuration in ASP.NET Core on the <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0&amp;WT.mc_id=DT-MVP-4024623" target="_blank">documentation</a> page.</p>

<h5 id="programcs">Program.cs</h5>

<p><em>Program.cs</em> functions just like it does for a console.  It serves as the entry point for your application. For the most part you are starting up the web host</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="rouge-code"><pre><span class="k">using</span> <span class="nn">Microsoft.AspNetCore</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Hosting</span><span class="p">;</span>

<span class="k">namespace</span> <span class="nn">Contacts.WebUi</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Program</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">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>
            <span class="nf">CreateWebHostBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">).</span><span class="nf">Build</span><span class="p">().</span><span class="nf">Run</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">public</span> <span class="k">static</span> <span class="n">IWebHostBuilder</span> <span class="nf">CreateWebHostBuilder</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">=&gt;</span>
            <span class="n">WebHost</span><span class="p">.</span><span class="nf">CreateDefaultBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
                <span class="p">.</span><span class="n">UseStartup</span><span class="p">&lt;</span><span class="n">Startup</span><span class="p">&gt;();</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h5 id="startupcs">Startup.cs</h5>

<p>The <em>Startup.cs</em> is where you configure your site.  The methods in the <code class="language-plaintext highlighter-rouge">Startup</code> class inform the hosting engine what services you are using.  ASP.NET Core has an <em>opt-in</em> model, meaning you tell it what you.  In previous versions of ASP.NET, the framework gave you everything. There are two methods in the <code class="language-plaintext highlighter-rouge">Startup</code> class; <code class="language-plaintext highlighter-rouge">Configure</code> and <code class="language-plaintext highlighter-rouge">ConfigureServices</code>.</p>

<h6 id="configure-method">Configure method</h6>

<p>The <code class="language-plaintext highlighter-rouge">Configure</code> method is used to configure the http pipeline.  A sample method looks like this.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">void</span> <span class="nf">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>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">env</span><span class="p">.</span><span class="nf">IsDevelopment</span><span class="p">())</span>
    <span class="p">{</span>
        <span class="n">app</span><span class="p">.</span><span class="nf">UseDeveloperExceptionPage</span><span class="p">();</span>
    <span class="p">}</span>
    <span class="k">else</span>
    <span class="p">{</span>
        <span class="n">app</span><span class="p">.</span><span class="nf">UseExceptionHandler</span><span class="p">(</span><span class="s">"/Home/Error"</span><span class="p">);</span>
        <span class="c1">// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.</span>
        <span class="n">app</span><span class="p">.</span><span class="nf">UseHsts</span><span class="p">();</span>
    <span class="p">}</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseHttpsRedirection</span><span class="p">();</span>
    <span class="n">app</span><span class="p">.</span><span class="nf">UseStaticFiles</span><span class="p">();</span>

    <span class="n">app</span><span class="p">.</span><span class="nf">UseRouting</span><span class="p">();</span>

    <span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>

    <span class="n">app</span><span class="p">.</span><span class="nf">UseEndpoints</span><span class="p">(</span><span class="n">endpoints</span> <span class="p">=&gt;</span>
    <span class="p">{</span>
        <span class="n">endpoints</span><span class="p">.</span><span class="nf">MapControllerRoute</span><span class="p">(</span>
            <span class="n">name</span><span class="p">:</span> <span class="s">"default"</span><span class="p">,</span>
            <span class="n">pattern</span><span class="p">:</span> <span class="s">"{controller=Home}/{action=Index}/{id?}"</span><span class="p">);</span>
    <span class="p">});</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In this definition, you’ll see that we are looking to see what environment we are running an displaying the appropriate error (lines 3-12). We then opt in to redirecting all requests to use https (line 13), allow the host to serve static files (line 14), use the default routing (line 16), use authorization (line 18), and finally use endpoints for MVC.  As you can see, we explicitly tell ASP.NET Core and the host how it should work instead of it making assumptions.</p>

<h6 id="configureservices">ConfigureServices</h6>

<p><code class="language-plaintext highlighter-rouge">ConfigureServices</code> is used to let ASP.NET know what services you are planning on using. The minimum for an ASP.NET Core MVC application would have <code class="language-plaintext highlighter-rouge">services.AddControllersWithView()</code>.  You could also register your application dependencies, logging, database context, and more.</p>

<h2 id="gotchas">Gotchas</h2>

<p>Here are a couple of things that stumped me once or twice migrating from ASP.NET Framework to ASP.NET Core. Hopefully, you don’t see them but if you do, try this!</p>

<h3 id="systemcomponentmodeldataannotation">System.ComponentModel.DataAnnotation</h3>

<p>The <code class="language-plaintext highlighter-rouge">System.ComponentModel.DataAnnotation</code> library is crucial in Entity Framework. This namespace used to be in the assembly/package for <code class="language-plaintext highlighter-rouge">System.ComponentModel</code>.  At one point in the evolution of .NET Framework, at least in version 4.7.2, <code class="language-plaintext highlighter-rouge">System.ComponentModel.DataAnnotation</code> was moved into it’s own assembly/package.  This change will only affect you if you migrate to ASP.NET Core MVC by keeping the .NET Framework ASP.NET MVC site going and working with .NET Standard, as I spoke to earlier.</p>

<h3 id="webconfig-target-framework">Web.Config target framework</h3>

<p>Some <em>web.config</em> files have the <code class="language-plaintext highlighter-rouge">targetFramework</code> set in them in addition to the <em>csproj</em> file.  Look for the <code class="language-plaintext highlighter-rouge">system.web</code> node in the configuration in the <em>web.config</em>, ensure the <code class="language-plaintext highlighter-rouge">compilation</code>, and <code class="language-plaintext highlighter-rouge">httpRuntime</code> nodes have the same <code class="language-plaintext highlighter-rouge">targetFramework</code> as your csproj.</p>

<h4 id="webconfig-snippet">Web.config snippet</h4>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre>  <span class="nt">&lt;system.web&gt;</span>
    <span class="nt">&lt;compilation</span> <span class="na">debug=</span><span class="s">"true"</span> <span class="na">targetFramework=</span><span class="s">"4.7.2"</span> <span class="nt">/&gt;</span>
    <span class="nt">&lt;httpRuntime</span> <span class="na">targetFramework=</span><span class="s">"4.7.2"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/system.web&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="csproj-snippet">csproj snippet</h4>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;TargetFrameworkVersion&gt;</span>v4.7.2<span class="nt">&lt;/TargetFrameworkVersion&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="using-net-standard-compilation-targets">Using .NET Standard (compilation targets)</h3>

<p>This gotcha is applicable if you will use .NET Standard to help with the migration and continue to work with the .NET Framework MVC application.</p>

<p>Once you first run an ASP.NET MVC framework application with a library reference to a library written in .NET standard, like the <code class="language-plaintext highlighter-rouge">Contacts.Model project</code>, you may see one or more errors. In Chrome or Microsoft Edge, you may get an “Unlimited” or “Too Many” redirects error message. This error will happen if you have custom error messages in your application.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;customErrors</span> <span class="na">mode=</span><span class="s">"On"</span> <span class="na">defaultRedirect=</span><span class="s">"ErrorPage.aspx?handler=customErrors"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;error</span> <span class="na">statusCode=</span><span class="s">"404"</span> <span class="na">redirect=</span><span class="s">"ErrorPage.aspx?handler=customErrors"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/customErrors&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Turn the custom errors off by changing the <code class="language-plaintext highlighter-rouge">mode</code> attribute to <code class="language-plaintext highlighter-rouge">Off</code>. If you refresh the browser, you will see a message saying, “System.Object is not found”. It’s a weird message because of <code class="language-plaintext highlighter-rouge">System.Object</code> is part of the both ASP.NET Core and ASP.NET. However, the error results from use referencing a .NET Standard project and not having a reference to .NET Standard in the .NET Framework application. After you add the reference, rerun the solution. It will still fail. Another weird one, the reason for this failure is IIS does not know how to load that assembly. So let’s tell it how to load it. Look for the <code class="language-plaintext highlighter-rouge">compilation\assemblies</code> node in your <em>web.config</em> and add the assembly.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;add</span> <span class="na">assembly=</span><span class="s">"netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51"</span><span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The node may look this after adding the assembly.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;compilation</span> <span class="na">debug=</span><span class="s">"true"</span> <span class="na">targetFramework=</span><span class="s">"4.7.2"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;assemblies&gt;</span>
        <span class="nt">&lt;add</span> <span class="na">assembly=</span><span class="s">"netstandard, Version=2.0.0.0, Culture=neutral,PublicKeyToken=cc7b13ffcd2ddd51"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/assemblies&gt;</span>
<span class="nt">&lt;/compilation&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong><em>Note</em></strong>: You may have other assemblies in this node depending on your application.</p>

<p>Now you should be execute and view the application. Remember to turn your custom errors back on.</p>

<h3 id="app_data">App_Data</h3>

<p>ASP.NET Core does not have the concept of an <em>App_Data</em> folder used in earlier versions of ASP.NET. <em>App_Data</em> has commonly used the identity database or dynamic app configuration. While you probably should store databases or database files on the webserver, it’s a common practice on development machines to have the application-specific databases in the <em>App_Data</em> folder. Although ASP.NET Core does not support this out of the box, you can do it with a bit of code.</p>

<p>The code for this workaround should go in the Startup.cs class.</p>

<p>First, you create a token or string in the <em>appsetting.development.json</em> file that we will replace with the folder the application is running in. Here, you’ll see, I added the <code class="language-plaintext highlighter-rouge">%CONTENTROOTPATH%</code> token as part of the <code class="language-plaintext highlighter-rouge">AttachDbFilename</code> property. <strong><em>Note</em></strong>: The name of the token can be anything you want.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
    </span><span class="nl">"ConnectionStrings"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"ContactsSqlServer"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Data Source=(LocalDB)</span><span class="se">\\</span><span class="s2">MSSQLLocalDB;
        AttachDbFilename=%CONTENTROOTPATH%</span><span class="se">\\</span><span class="s2">App_Data</span><span class="se">\\</span><span class="s2">contacts.mdf;
        Integrated Security=True"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Next, in the <em>Startup.cs</em> file, you need to create a variable to hold the path to the content.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">private</span> <span class="kt">string</span> <span class="n">_contentPath</span> <span class="p">=</span> <span class="s">""</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Next, you’ll need to update the constructor of the <code class="language-plaintext highlighter-rouge">Startup</code> class to have ASP.NET Core inject the configuration and web host environment.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="k">private</span> <span class="kt">string</span> <span class="n">_contentRootPath</span> <span class="p">=</span> <span class="s">""</span><span class="p">;</span>
<span class="k">public</span> <span class="nf">Startup</span><span class="p">(</span><span class="n">IConfiguration</span> <span class="n">configuration</span><span class="p">,</span> <span class="n">IWebHostEnvironment</span> <span class="n">env</span><span class="p">)</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="n">_contentRootPath</span> <span class="p">=</span> <span class="n">env</span><span class="p">.</span><span class="n">ContentRootPath</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then in the <code class="language-plaintext highlighter-rouge">ConfigureServices</code>, before you need to use the <em>App_Data</em> folder.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="kt">string</span> <span class="n">connectionString</span> <span class="p">=</span> <span class="n">Configuration</span><span class="p">.</span><span class="nf">GetConnectionString</span><span class="p">(</span><span class="s">"ContactsSqlServer"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="n">connectionString</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="s">"%CONTENTROOTPATH%"</span><span class="p">))</span>
<span class="p">{</span>
    <span class="n">connectionString</span> <span class="p">=</span> <span class="n">connectionString</span><span class="p">.</span><span class="nf">Replace</span><span class="p">(</span><span class="s">"%CONTENTROOTPATH%"</span><span class="p">,</span> <span class="n">_contentRootPath</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>When you add the Db Context in <code class="language-plaintext highlighter-rouge">ConfigureServices</code>, replace the code with.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">services</span><span class="p">.</span><span class="n">AddDbContext</span><span class="p">&lt;</span><span class="n">Data</span><span class="p">.</span><span class="n">ContactsContext</span><span class="p">&gt;(</span><span class="n">options</span> <span class="p">=&gt;</span> <span class="p">{</span>  <span class="n">options</span><span class="p">.</span><span class="nf">UseSqlServer</span><span class="p">(</span><span class="n">connectionString</span><span class="p">);});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now copy <em>App_Data</em> folder from your previous project to the new one.</p>

<h3 id="identity">Identity</h3>

<p>If you used identity management in ASP.NET MVC Framework, you need to update a couple of things, primarily if you used Entity Framework to assist.</p>

<p>In ASP.NET MVC, authentication and identity features are configured using ASP.NET Identity in <em>Startup.Auth.cs</em> and <em>IdentityConfig.cs</em>, located in the <em>App_Start</em> folder. In ASP.NET Core MVC, these features are configured in <em>Startup.cs</em>.</p>

<p>Install the following NuGet packages:</p>

<ul>
  <li>Microsoft.AspNetCore.Identity.EntityFrameworkCore</li>
  <li>Microsoft.AspNetCore.Authentication.Cookies</li>
  <li>Microsoft.EntityFrameworkCore.SqlServer</li>
</ul>

<p>Then you’ll need to configure identity in the <code class="language-plaintext highlighter-rouge">Startup.ConfigureServices</code> method of <em>Startup.cs</em>. Something like this</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">void</span> <span class="nf">ConfigureServices</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span>
<span class="p">{</span>
    <span class="c1">// Add EF services to the services container.</span>
    <span class="n">services</span><span class="p">.</span><span class="n">AddDbContext</span><span class="p">&lt;</span><span class="n">ApplicationDbContext</span><span class="p">&gt;(</span><span class="n">options</span> <span class="p">=&gt;</span>
        <span class="n">options</span><span class="p">.</span><span class="nf">UseSqlServer</span><span class="p">(</span><span class="n">Configuration</span><span class="p">.</span><span class="nf">GetConnectionString</span><span class="p">(</span><span class="s">"DefaultConnection"</span><span class="p">)));</span>

    <span class="n">services</span><span class="p">.</span><span class="n">AddIdentity</span><span class="p">&lt;</span><span class="n">ApplicationUser</span><span class="p">,</span> <span class="n">IdentityRole</span><span class="p">&gt;()</span>
        <span class="p">.</span><span class="n">AddEntityFrameworkStores</span><span class="p">&lt;</span><span class="n">ApplicationDbContext</span><span class="p">&gt;()</span>
        <span class="p">.</span><span class="nf">AddDefaultTokenProviders</span><span class="p">();</span>

     <span class="n">services</span><span class="p">.</span><span class="nf">AddMvc</span><span class="p">();</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You can read more on it at <a href="https://docs.microsoft.com/en-us/aspnet/core/migration/identity?view=aspnetcore-3.0&amp;WT.mc_id=DT-MVP-4024623" target="_blank">Migrate Authentication and Identity to ASP.NET Core</a> or <a href="https://medium.com/@nativoplus/asp-net-core-identity-3-0-6018fc151b4" target="_blank">ASP.NET Core Identity 3.0 : Modifying the Identity Database</a></p>

<h2 id="wrap-up">Wrap up</h2>

<p>That’s it! That’s a lot to take. While I can’t cover every possible scenario that you might hit, hopefully, you have enough to get you started and handle some of the surprises that I ran into while migrating applications.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="ASP.NET" /><category term="ASP.NET Core" /><category term="ASP.NET Core MVC" /><category term="Razor" /><category term="MVC" /><category term="Web" /><category term=".NET Framework" /><category term=".NET" /><summary type="html"><![CDATA[.NET has been around for a while and has had many versions. We started the .NET Framework, moved into .NET Core, and now just .NET, kind of sort of :smile:. Keeping your application up with the latest version of .NET can be challenging. There are budgets to consider, time to learn the differences, and time to “fit in the migration.” Migrating or “upgrading” an ASP.NET MVC from .NET Framework to ASP.NET Core can be easy at times. Other times it can be just hard. There are no silver bullets to the migration as each project and solution is different. I’m going to walk you through one of the ways you can have a successful migration from ASP.NET MVC to ASP.NET Core MVC. At the end of the post, I’ll show you some gotchas that I have run across migrating applications.]]></summary></entry><entry><title type="html">ASP.NET Core 5 and Angular - Book Review</title><link href="https://www.josephguadagno.net/2021/04/22/asp-net-core-5-and-angular-book-review" rel="alternate" type="text/html" title="ASP.NET Core 5 and Angular - Book Review" /><published>2021-04-22T15:30:00-07:00</published><updated>2021-04-22T15:30:00-07:00</updated><id>https://www.josephguadagno.net/2021/04/22/asp-net-core-5-and-angular-book-review</id><content type="html" xml:base="https://www.josephguadagno.net/2021/04/22/asp-net-core-5-and-angular-book-review"><![CDATA[<p>Overall I enjoyed the outline and flow of this book.  The author does an excellent job explaining things and makes no assumptions that the reader “knows it.” As a result, an experienced reader might skip parts of the content and still get what the author is teaching.</p>

<p>I like how this book breaks down a “real world” application into pieces (chapters).  The author starts by introducing the languages, technologies, and tools.  After the introduction, you begin to build an application. The author presents how to use ASP.NET Core as the “back-end,” then transitions to using Angular for the “front-end.”  From there on out, the application is built capability by capability. The author covers building out the database, including the server and building out the API to retrieve data from the database, and then consuming that data via the Angular client.  The author then discusses editing, updating, paging, sorting, and filtering the data—Lots of capabilities that we find in everyday applications.</p>

<p>For the following few chapters, the author makes improvements to the code base to make it more maintainable and testable. Then the author talks about authentication and authorization.  The book is wrapped up by showing how to deploy your application to Windows, Linux, or Azure App Service.</p>

<p>Overall, it is an excellent introduction to ASP.NET 5 Core and Angular.  As a bonus, in every chapter, the author has a recommended reading section and reference section for more details.</p>

<p>This book would be a great addition to a web developer who wants to get into ASP.NET and Angular.</p>

<p>Get <a href="https://amzn.to/3ndwN4R" target="_blank">ASP.NET Core 5 and Angular</a> on Amazon.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Book Reviews" /><category term="ASP.NET" /><category term="ASP.NET Core" /><category term="ASP.NET Core MVC" /><category term="Angular" /><summary type="html"><![CDATA[Overall I enjoyed the outline and flow of this book. The author does an excellent job explaining things and makes no assumptions that the reader “knows it.” As a result, an experienced reader might skip parts of the content and still get what the author is teaching.]]></summary></entry><entry><title type="html">Creating a Search Suggestion widget using Azure Maps Search Service and KendoUI Autocomplete.</title><link href="https://www.josephguadagno.net/2021/03/27/creating-a-search-suggestion-widget-using-azure-maps-search-service-and-kendoui-autocomplete" rel="alternate" type="text/html" title="Creating a Search Suggestion widget using Azure Maps Search Service and KendoUI Autocomplete." /><published>2021-03-27T10:20:00-07:00</published><updated>2021-03-27T10:20:00-07:00</updated><id>https://www.josephguadagno.net/2021/03/27/creating-a-search-suggestion-widget-using-azure-maps-search-service-and-kendoui-autocomplete</id><content type="html" xml:base="https://www.josephguadagno.net/2021/03/27/creating-a-search-suggestion-widget-using-azure-maps-search-service-and-kendoui-autocomplete"><![CDATA[<p>When using Google or Bing Maps to lookup an address or point of interest, both display address suggestions in a user-friendly way. In the example below, I type “Time Square”, and Bing suggests a few addresses/places.</p>

<p><img src="/assets/images/posts/kendo-autocomplete-bing-search.png" alt="Bing Autocomplete Suggestions" class="align-center" /></p>

<p>In this post, I’ll walk you through how you can implement the same functionality using HTML, JavaScript, <a href="https://docs.microsoft.com/en-us/rest/api/maps/search?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Map Search</a> service, and <a href="https://demos.telerik.com/kendo-ui/" target="_blank">Telerik Kendo UI</a> <a href="https://demos.telerik.com/kendo-ui/autocomplete/index" target="_blank">Autocomplete</a> control.  You’ll be able to download the completed code at the end of the post.</p>

<p>If you want to watch me do this “live” instead, check out the <a href="https://youtu.be/cmpzZQa76rs" target="_blank">video</a>.</p>

<h2 id="software-versions">Software Versions</h2>

<p>This post was written for the following software and versions listed below.</p>

<ul>
  <li><a href="https://www.telerik.com/kendo-ui" target="_blank">Kendo UI</a> - version 2021.1.224</li>
  <li><a href="https://docs.microsoft.com/en-us/rest/api/maps/search?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Map Search</a> service - version 1.0</li>
</ul>

<h2 id="getting-started">Getting Started</h2>

<p>The post assumes you already have an Azure Maps account with a corresponding Primary Key and/or Client Id. If you don’t have a key, you can obtain one <a href="https://docs.microsoft.com/en-us/azure/azure-maps/quick-demo-map-app#get-the-primary-key-for-your-account?wt.mc_id=DT-MVP-4024623" target="_blank">here</a>. In addition to an Azure Maps account, you need to have a licensed copy of the <a href="https://www.telerik.com/purchase/kendo-ui" target="_blank">Telerik Kendo UI</a> suite.</p>

<p>If you are ready, open up your IDE of choice, Visual Studio, Visual Studio Code, JetBrains Rider, or just plan Notepad/TextEdit to get started.</p>

<h3 id="html">HTML</h3>

<p>Start with the HTML file.  Create a file and name it <code class="language-plaintext highlighter-rouge">autocomplete.html.</code>  Create the standard <code class="language-plaintext highlighter-rouge">&lt;HTML&gt;</code>, <code class="language-plaintext highlighter-rouge">&lt;HEAD&gt;</code>, and <code class="language-plaintext highlighter-rouge">&lt;BODY&gt;</code> tags.</p>

<p>In the <code class="language-plaintext highlighter-rouge">&lt;HEAD&gt;</code> section, we are going to need to register the stylesheets and Javascript files for both the Kendo UI libraries and the Azure Maps.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/styles/kendo.common.min.css"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/styles/kendo.office365.min.css"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/styles/kendo.office365.mobile.min.css"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css"</span> <span class="na">type=</span><span class="s">"text/css"</span><span class="nt">&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/js/jquery.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/js/kendo.all.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>  
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>NOTE</em></strong>: The version number for the Kendo UI library at the time of this post was <strong>2021.1.224</strong>, you may have a different version, but that should be fine.</p>

<p>You’ll then need to add the references to our script files.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"azurekey.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"autocomplete.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now in the <code class="language-plaintext highlighter-rouge">&lt;BODY&gt;</code> tag, add our components, a <code class="language-plaintext highlighter-rouge">&lt;input&gt;</code> and a <code class="language-plaintext highlighter-rouge">&lt;div&gt;</code></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"queryText"</span> <span class="na">style=</span><span class="s">"width: 100%;"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;p&gt;&lt;/p&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mapControl"</span> <span class="na">style=</span><span class="s">"position: relative;width:100%;min-width:290px;height:500px;"</span><span class="nt">&gt;&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Feel free to style this however you want; I did it this way to help accent stuff in the UI.  The critical part of the HTML is having an <code class="language-plaintext highlighter-rouge">&lt;input&gt;</code> with the name of <code class="language-plaintext highlighter-rouge">querytext</code> and a <code class="language-plaintext highlighter-rouge">div</code> with the <code class="language-plaintext highlighter-rouge">id</code> of <code class="language-plaintext highlighter-rouge">mapcontrol</code>.</p>

<p>The completed HTML file should look something like this.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;html&gt;</span>
    <span class="nt">&lt;head&gt;</span>
        <span class="nt">&lt;title&gt;</span>Autocomplete Demo<span class="nt">&lt;/title&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/styles/kendo.common.min.css"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/styles/kendo.office365.min.css"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/styles/kendo.office365.mobile.min.css"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css"</span> <span class="na">type=</span><span class="s">"text/css"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/js/jquery.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
        <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://kendo.cdn.telerik.com/2021.1.224/js/kendo.all.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
        <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
    
        <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"azurekey.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
        <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"autocomplete.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
    <span class="nt">&lt;/head&gt;</span>
    <span class="nt">&lt;body&gt;</span>
        <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"queryText"</span> <span class="na">style=</span><span class="s">"width: 100%;"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;p&gt;&lt;/p&gt;</span>
        <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mapControl"</span> <span class="na">style=</span><span class="s">"position: relative;width:100%;min-width:290px;height:500px;"</span><span class="nt">&gt;&lt;/div&gt;</span>
    <span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="the-javascript">The Javascript</h3>

<p>Now let’s focus on the Javascript file.  You’ll need to create two files, <code class="language-plaintext highlighter-rouge">azurekey.js</code> and <code class="language-plaintext highlighter-rouge">autocomplete.js</code>.  You don’t need to create an <code class="language-plaintext highlighter-rouge">azurekey.js</code> if you don’t want to, but since this has a key, I exclude it from source control.</p>

<h4 id="azurekeyjs">AzureKey.js</h4>

<p>This file has one line in it which sets up a variable named <code class="language-plaintext highlighter-rouge">azurekey</code> used throughout <code class="language-plaintext highlighter-rouge">autocomplete.js</code> for our Azure Maps integration. The contents of the file should be.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kd">const</span> <span class="nx">azureKey</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">replace me</span><span class="dl">"</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Replace the <code class="language-plaintext highlighter-rouge">replace me</code> with your Azure Maps Client Id or Primary Key.</p>

<h4 id="autocompletejs">Autocomplete.js</h4>

<p>We are going to have a few variables and two functions in the files.  Let’s start with some of the variables.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="kd">let</span> <span class="nx">map</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">azureSearchDataSource</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">azureMapDataSource</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">mapCenter</span> <span class="o">=</span> <span class="p">[</span><span class="o">-</span><span class="mf">73.985130</span><span class="p">,</span> <span class="mf">40.758896</span><span class="p">]</span>
<span class="kd">const</span> <span class="nx">defaultZoom</span> <span class="o">=</span> <span class="mi">15</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Variable Name</th>
      <th>Type</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">map</code></td>
      <td>The Azure Map map</td>
      <td>Display the map control</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">azureSearchDataSource</code></td>
      <td><a href="https://docs.telerik.com/kendo-ui/framework/datasource/overview" target="_blank">Kendo UI DataSource</a></td>
      <td>Used to call the Azure Map Search service from the Kendo UI Autocomplete control</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">azureMapDataSource</code></td>
      <td><a href="https://docs.microsoft.com/en-us/azure/azure-maps/create-data-source-web-sdk?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Maps DataSource</a></td>
      <td>Used to draw the pushpins on the layers of the map</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">mapCenter</code></td>
      <td>Array of numbers</td>
      <td>Used to center the map and provide hints of where to search. The first number is the longitude and the second is the latitude. In this example, -73.985130, 40.758896 is Time Square, Manhattan, NY</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">defaultZoom</code></td>
      <td>A number or string</td>
      <td>Used to tell the map control at what level do we want the map control to zoom in</td>
    </tr>
  </tbody>
</table>

<p>Once the variables are there, we create a function, <code class="language-plaintext highlighter-rouge">initializeMap</code>, which looks like this.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="rouge-code"><pre><span class="kd">function</span> <span class="nx">initializeMap</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">map</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">atlas</span><span class="p">.</span><span class="nb">Map</span><span class="p">(</span><span class="dl">'</span><span class="s1">mapControl</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
        <span class="na">center</span><span class="p">:</span> <span class="nx">mapCenter</span><span class="p">,</span>
        <span class="na">zoom</span><span class="p">:</span> <span class="nx">defaultZoom</span><span class="p">,</span>
        <span class="na">authOptions</span><span class="p">:</span> <span class="p">{</span>
            <span class="na">authType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">subscriptionKey</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">subscriptionKey</span><span class="p">:</span> <span class="nx">azureKey</span>
        <span class="p">}</span>
    <span class="p">});</span>

    <span class="nx">map</span><span class="p">.</span><span class="nx">events</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">ready</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
        <span class="nx">azureMapDataSource</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">atlas</span><span class="p">.</span><span class="nx">source</span><span class="p">.</span><span class="nx">DataSource</span><span class="p">();</span>
        <span class="nx">map</span><span class="p">.</span><span class="nx">sources</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">azureMapDataSource</span><span class="p">);</span>
        <span class="nx">map</span><span class="p">.</span><span class="nx">layers</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="k">new</span> <span class="nx">atlas</span><span class="p">.</span><span class="nx">layer</span><span class="p">.</span><span class="nx">SymbolLayer</span><span class="p">(</span><span class="nx">azureMapDataSource</span><span class="p">));</span>
        <span class="nx">azureMapDataSource</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="k">new</span> <span class="nx">atlas</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">Point</span><span class="p">(</span><span class="nx">mapCenter</span><span class="p">));</span>
    <span class="p">})</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In lines 1-9, we initialize the map control.</p>

<p>Line 2 is the name of the <code class="language-plaintext highlighter-rouge">div</code> you want the map to be rendered in.  You’ll notice that this name matches the <code class="language-plaintext highlighter-rouge">div</code> we created on the HTML page.</p>

<p>Line 3 and 4 use the variables we created in the previous step to center the map and set the zoom level.</p>

<p>Lines 5 - 7, we create the map authentication. For more details on the map’s customization, check out the Azure Map documentation on <a href="https://docs.microsoft.com/en-us/azure/azure-maps/map-create?wt.mc_id=DT-MVP-4024623" target="_blank">creating a map</a>.</p>

<p>In lines 11 - 16, we attach a <code class="language-plaintext highlighter-rouge">ready</code> event to map control, which instructs the Azure Maps controls to execute the code when the map is ready, meaning displayed.</p>

<p>Lines 12 - 15, we add a data source to the map, which has a <a href="https://docs.microsoft.com/en-us/azure/azure-maps/map-add-pin?wt.mc_id=DT-MVP-4024623" target="_blank">Symbol Layer</a> in it. This is done so we can draw a pushpin on the center of the map.</p>

<p>We’re almost there!</p>

<p>The next step is to initialize the map on the page when the document is ready.  To do that, let’s attach to the ready event with jquery.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nx">$</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">initializeMap</span><span class="p">();</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>At this point, if you save the 3 pages and open up <code class="language-plaintext highlighter-rouge">autocomplete.html</code> in a browser, you should see something like this.</p>

<p><img src="/assets/images/posts/kendo-autocomplete-initialized-map.png" alt="Initialized Azure Map" class="align-center" /></p>

<h2 id="assembling-the-autocomplete-control">Assembling the Autocomplete Control</h2>

<p>Before we assemble the components to enable the address and point of interest suggestions, let’s take a quick look at the Azure Maps Search service API.</p>

<h3 id="the-azure-maps-search-service-request">The Azure Maps Search service request</h3>

<p>The calls to the API are done via an HTTP GET with query parameters.  The breakdown of the parameters is as follows.</p>

<h4 id="url-root">URL Root</h4>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Value</th>
      <th>Comments</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Domain</td>
      <td><code class="language-plaintext highlighter-rouge">https://atlas.microsoft.com</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Endpoint</td>
      <td><code class="language-plaintext highlighter-rouge">/search/poi/</code></td>
      <td>The search for Points of Interests endpoint</td>
    </tr>
    <tr>
      <td>Return Type</td>
      <td><code class="language-plaintext highlighter-rouge">json</code></td>
      <td>Desired format of the response. Value can be either json or xml.</td>
    </tr>
  </tbody>
</table>

<h4 id="parameters">Parameters</h4>

<table>
  <tbody>
    <tr>
      <td>Parameter Name</td>
      <td>Example Value</td>
      <td>Comments</td>
    </tr>
    <tr>
      <td>typeahead</td>
      <td><code class="language-plaintext highlighter-rouge">true</code></td>
      <td>Boolean. If the typeahead flag is set, the query will be interpreted as a partial input and the search will enter predictive mode</td>
    </tr>
    <tr>
      <td>api-version</td>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
      <td>Version number of Azure Maps API. Current version is 1.0</td>
    </tr>
    <tr>
      <td>view</td>
      <td><code class="language-plaintext highlighter-rouge">Auto</code></td>
      <td>The View parameter specifies which set of geopolitically disputed content is returned via Azure Maps services, including borders and labels displayed on the map.</td>
    </tr>
    <tr>
      <td>language</td>
      <td><code class="language-plaintext highlighter-rouge">en-US</code></td>
      <td>Language in which search results should be returned.</td>
    </tr>
    <tr>
      <td>countrySet</td>
      <td><code class="language-plaintext highlighter-rouge">US</code></td>
      <td>Comma separated string of country codes</td>
    </tr>
    <tr>
      <td>subscription-key</td>
      <td><em>your subscription key</em></td>
      <td> </td>
    </tr>
    <tr>
      <td>lon</td>
      <td><code class="language-plaintext highlighter-rouge">-73.98513</code></td>
      <td>The longitude of the center of the search area</td>
    </tr>
    <tr>
      <td>lat</td>
      <td><code class="language-plaintext highlighter-rouge">40.758896</code></td>
      <td>The latitude of the center of the search area</td>
    </tr>
    <tr>
      <td>query</td>
      <td><code class="language-plaintext highlighter-rouge">macy</code></td>
      <td>The address/place you are searching for</td>
    </tr>
  </tbody>
</table>

<p>Sample search query</p>

<p><code class="language-plaintext highlighter-rouge">https://atlas.microsoft.com/search/poi/json?typeahead=true&amp;api-version=1&amp;view=Auto&amp;language=en-US&amp;countrySet=US&amp;subscription-key=replace-me&amp;lon=-73.985130&amp;lat=40.758896&amp;query=macy</code></p>

<p>For more details on the search parameters available, please look at the <a href="https://docs.microsoft.com/en-us/rest/api/maps/search/getsearchpoi?wt.mc_id=DT-MVP-4024623" target="_blank">Get Search POI</a> documentation.</p>

<h3 id="the-azure-maps-search-service-response">The Azure Maps Search service response</h3>

<p>Assuming the request is correct, the service responds with JSON data. The response has two properties <code class="language-plaintext highlighter-rouge">summary</code> and <code class="language-plaintext highlighter-rouge">results</code>. The <code class="language-plaintext highlighter-rouge">summary</code> section is just that, a summary of the requests and the results defined as a <a href="https://docs.microsoft.com/en-us/rest/api/maps/search/getsearchpoi#searchpoisummary" target="_blank">SearchPoiSummary</a>.  The <code class="language-plaintext highlighter-rouge">results</code> is an array of <a href="https://docs.microsoft.com/en-us/rest/api/maps/search/getsearchpoi#searchpoiresult?wt.mc_id=DT-MVP-4024623" target="_blank">SearchPoiResult</a> items.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"summary"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"query"</span><span class="p">:</span><span class="w"> </span><span class="s2">"macy"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"queryType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NON_NEAR"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"queryTime"</span><span class="p">:</span><span class="w"> </span><span class="mi">51</span><span class="p">,</span><span class="w">
    </span><span class="nl">"numResults"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w">
    </span><span class="nl">"offset"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
    </span><span class="nl">"totalResults"</span><span class="p">:</span><span class="w"> </span><span class="mi">960</span><span class="p">,</span><span class="w">
    </span><span class="nl">"fuzzyLevel"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
    </span><span class="nl">"geoBias"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">40.758896</span><span class="p">,</span><span class="w">
      </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">-73.98513</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"results"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"POI"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"g6JpZK84NDAzNjkwMTA3NzI0NjOhY6NVU0GhdqdVbmlmaWVk"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"score"</span><span class="p">:</span><span class="w"> </span><span class="mf">2.5242369175</span><span class="p">,</span><span class="w">
      </span><span class="nl">"dist"</span><span class="p">:</span><span class="w"> </span><span class="mf">973.6298458107448</span><span class="p">,</span><span class="w">
      </span><span class="nl">"info"</span><span class="p">:</span><span class="w"> </span><span class="s2">"search:ta:840369010772463-US"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"poi"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Macy's Department Store"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"phone"</span><span class="p">:</span><span class="w"> </span><span class="s2">"+1 212-695-4400"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"categorySet"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">7376</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">],</span><span class="w">
        </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"www.macys.com"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"categories"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="s2">"important tourist attraction"</span><span class="w">
        </span><span class="p">],</span><span class="w">
        </span><span class="nl">"classifications"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IMPORTANT_TOURIST_ATTRACTION"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"names"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
              </span><span class="p">{</span><span class="w">
                </span><span class="nl">"nameLocale"</span><span class="p">:</span><span class="w"> </span><span class="s2">"en-US"</span><span class="p">,</span><span class="w">
                </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"important tourist attraction"</span><span class="w">
              </span><span class="p">}</span><span class="w">
            </span><span class="p">]</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"streetNumber"</span><span class="p">:</span><span class="w"> </span><span class="s2">"151"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"streetName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"W 34Th St"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"municipalitySubdivision"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Manhattan"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"municipality"</span><span class="p">:</span><span class="w"> </span><span class="s2">"New York"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"countrySecondarySubdivision"</span><span class="p">:</span><span class="w"> </span><span class="s2">"New York"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"countrySubdivision"</span><span class="p">:</span><span class="w"> </span><span class="s2">"NY"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"countrySubdivisionName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"New York"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"postalCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10001"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"extendedPostalCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"10001-2101"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"countryCode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"US"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"country"</span><span class="p">:</span><span class="w"> </span><span class="s2">"United States"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"countryCodeISO3"</span><span class="p">:</span><span class="w"> </span><span class="s2">"USA"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"freeformAddress"</span><span class="p">:</span><span class="w"> </span><span class="s2">"151 W 34Th St, New York, NY 10001"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"localName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"New York"</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"position"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">40.75042</span><span class="p">,</span><span class="w">
        </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">-73.98803</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"viewport"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"topLeftPoint"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">40.75231</span><span class="p">,</span><span class="w">
          </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">-73.99052</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"btmRightPoint"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">40.74853</span><span class="p">,</span><span class="w">
          </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">-73.98554</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"entryPoints"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"main"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"position"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">40.75092</span><span class="p">,</span><span class="w">
            </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">-73.99043</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="p">{</span><span class="w">
          </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"main"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"position"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
            </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">40.75046</span><span class="p">,</span><span class="w">
            </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">-73.98934</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>While there is a lot of data that we can use, we will only use the <code class="language-plaintext highlighter-rouge">address.freeformAddress</code>, <code class="language-plaintext highlighter-rouge">poi</code>, <code class="language-plaintext highlighter-rouge">poi.name</code>, and <code class="language-plaintext highlighter-rouge">position</code> properties.</p>

<h3 id="kendo-ui">Kendo UI</h3>

<p>Now let’s turn the <code class="language-plaintext highlighter-rouge">querytext</code> <code class="language-plaintext highlighter-rouge">input</code> control into a jquery Kendo UI Autocomplete widget.  Let’s go back to the <code class="language-plaintext highlighter-rouge">autocomplete.js</code> file and before the <code class="language-plaintext highlighter-rouge">initializeMap();</code> statement and after the <code class="language-plaintext highlighter-rouge">$(() ==&gt; {</code> statement place the following code.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#queryText</span><span class="dl">'</span><span class="p">).</span><span class="nx">kendoAutoComplete</span><span class="p">({</span>
    <span class="na">minLength</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
    <span class="na">placeholder</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Select a venue</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">dataValueField</span><span class="p">:</span> <span class="dl">"</span><span class="s2">id</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">dataTextField</span><span class="p">:</span> <span class="dl">"</span><span class="s2">poi.name</span><span class="dl">"</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This turns the <code class="language-plaintext highlighter-rouge">input</code> control into an Autocomplete widget.</p>

<p>Line 2 tells the widget to start the lookup only when there is a minimum of 3 characters entered.</p>

<p>Line 3 is the text that gets displayed when there is no input to prompt the user what you are looking for.</p>

<p>Line 4 is the field to use as the value for <em>selected</em>. The value is helpful for lookups and storage later.</p>

<p>Line 5 is the text field to display in the control once an item is selected.</p>

<p>For additional configuration items for the Autocomplete widget, check out the <a href="https://docs.telerik.com/kendo-ui/api/javascript/ui/autocomplete" target="_blank">documentation</a>.</p>

<h3 id="datasource">DataSource</h3>

<p>Now that widget is created, we need to create a data source to connect the Autocomplete widget with the Azure Maps Search service.  This is where the shared utility of the <a href="https://docs.telerik.com/kendo-ui/framework/datasource/overview" target="_blank">DataSource</a> comes in.  I’m not going to go into great detail of the utility because their documentation is great.</p>

<p>Let’s go and create the DataSource.  Just before the <code class="language-plaintext highlighter-rouge">$('#queryText').kendoAutoComplete({</code> code we are going to create and config the DataSource utility.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nx">azureSearchDataSource</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">kendo</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">DataSource</span><span class="p">({});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This initializes the DataSource utility.  Now, let’s config it.</p>

<p>In between the curly braces <code class="language-plaintext highlighter-rouge">{}</code>, we are going to add the <code class="language-plaintext highlighter-rouge">serverFiltering</code> property and set it to <code class="language-plaintext highlighter-rouge">true</code>.  This is <strong><em>very</em></strong> important. Setting the <code class="language-plaintext highlighter-rouge">serverFiltering</code> to <code class="language-plaintext highlighter-rouge">true</code> instructs the DataSource that it needs to get the data from the server by making another call any time the search (or input changes). Otherwise, the control will filter with a locally cache version of the data set. In this case, we don’t want to use a locally cache copy because the data will most likely not match.</p>

<p>Next, we have to set the <code class="language-plaintext highlighter-rouge">transport</code> property.  The <a href="https://docs.telerik.com/kendo-ui/api/javascript/data/datasource/configuration/transport" target="_blank">transport</a> is used to interact with the data source, in our case, the Azure Maps Search service.  We are going to want to configure the <code class="language-plaintext highlighter-rouge">read</code> property of the <code class="language-plaintext highlighter-rouge">transport</code> object.  Since the DataSource utility supports CRUD operations, the <code class="language-plaintext highlighter-rouge">transport</code> object supports read, write, update, and delete.</p>

<p>Our transport section looks like this.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre><span class="nx">transport</span><span class="p">:</span> <span class="p">{</span>
  <span class="nl">read</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">url</span><span class="p">:</span> <span class="dl">"</span><span class="s2">https://atlas.microsoft.com/search/poi/json?typeahead=true&amp;api-version=1&amp;view=Auto&amp;language=en-US&amp;countrySet=US&amp;subscription-key=</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">azureKey</span><span class="p">,</span>
    <span class="na">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">get</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">dataType</span><span class="p">:</span> <span class="dl">"</span><span class="s2">json</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">data</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">center</span> <span class="o">=</span> <span class="nx">map</span><span class="p">.</span><span class="nx">getCamera</span><span class="p">().</span><span class="nx">center</span><span class="p">;</span>
        <span class="kd">var</span> <span class="nx">searchTerm</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="dl">"</span><span class="s2">#queryText</span><span class="dl">"</span><span class="p">).</span><span class="nx">data</span><span class="p">(</span><span class="dl">"</span><span class="s2">kendoAutoComplete</span><span class="dl">"</span><span class="p">).</span><span class="nx">value</span><span class="p">();</span>
        <span class="k">return</span> <span class="p">{</span>
            <span class="na">lon</span><span class="p">:</span> <span class="nx">center</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span>
            <span class="na">lat</span><span class="p">:</span> <span class="nx">center</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span>
            <span class="na">query</span><span class="p">:</span> <span class="nx">searchTerm</span>
        <span class="p">}</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">url</code> property sets the URL for the read property.  In our case, we populated most of the fields.  Any dynamic fields are handled with <code class="language-plaintext highlighter-rouge">data</code> property. The <code class="language-plaintext highlighter-rouge">type</code> and <code class="language-plaintext highlighter-rouge">dataType</code> properties do not need to be set; they are the default. It is set here for clarity.  As I alluded to, the <code class="language-plaintext highlighter-rouge">data</code> property returns a JSON object which the DataSource utility stringify and appends to the URL.  In the code above, on line 7, we create a variable called <code class="language-plaintext highlighter-rouge">center</code>, which asks the Azure Maps map control for the center of the map in case the user moved it. Line 8, we extract the value of the <code class="language-plaintext highlighter-rouge">queryText</code> control to get the search term. Lines 9 - 12, we return an object that looks like this</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w"> 
  </span><span class="nl">"lon"</span><span class="p">:</span><span class="w"> </span><span class="mf">-73.98516</span><span class="p">,</span><span class="w">
  </span><span class="nl">"lat"</span><span class="p">:</span><span class="w"> </span><span class="mf">40.758896</span><span class="p">,</span><span class="w"> 
  </span><span class="nl">"query"</span><span class="p">:</span><span class="w"> </span><span class="s2">"macy"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>This then gets stringified to <code class="language-plaintext highlighter-rouge">&amp;lon=-73.98516&amp;lat=40.758896&amp;query=macy</code>.</p>

<p>Since the Azure Map Search service does not return an array of results, we have to tell the DataSource what the <em>schema</em> of the data is. Let’s add a <code class="language-plaintext highlighter-rouge">schema</code> property to the DataSource that looks like this.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="nx">schema</span><span class="p">:</span> <span class="p">{</span>
    <span class="nl">type</span><span class="p">:</span> <span class="dl">"</span><span class="s2">json</span><span class="dl">"</span><span class="p">,</span>
    <span class="nx">data</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nx">results</span><span class="p">;</span>
    <span class="p">},</span>
    <span class="nx">model</span><span class="p">:</span> <span class="p">{</span>
        <span class="nl">id</span><span class="p">:</span> <span class="dl">"</span><span class="s2">id</span><span class="dl">"</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Line 2 tells the DataSource to expect <code class="language-plaintext highlighter-rouge">json</code> data.</p>

<p>For line 3, we create a function for the <code class="language-plaintext highlighter-rouge">data</code> property.  This property identifies what the DataSource should consider the result.  Since the Azure Maps Search service returns it as a property of the response, we return <code class="language-plaintext highlighter-rouge">response.results</code> from the function.</p>

<p>In lines 6-8, we define the model.  The model is essential if you are doing CRUD operations, and the structure of the data for editors is essential.  For our use case, we just need to identify the <code class="language-plaintext highlighter-rouge">id</code> to the <code class="language-plaintext highlighter-rouge">id</code> of the result item.</p>

<p>Now, you can go back to the initialization of the AutoComplete widget and assign set the property <code class="language-plaintext highlighter-rouge">dataSource</code> to <code class="language-plaintext highlighter-rouge">azureSearchDataSource</code>.</p>

<h3 id="template">Template</h3>

<p>Now that we know the data structure returned let’s display some additional data in the search suggestion.  For this example, I would like to display the name of the suggestion, the <code class="language-plaintext highlighter-rouge">poi.name</code>, and the address <code class="language-plaintext highlighter-rouge">address.freeformAddress</code>. Fortunately, the Autocomplete widget, and many other widgets in the suite, provide a templating engine to help change the look.  We are going to use the <a href="https://docs.telerik.com/kendo-ui/api/javascript/ui/autocomplete/configuration/template" target="_blank">template</a> property.</p>

<p>Explaining everything possible is well beyond this blog post, so I’ll cover some of our use.  Somewhere in the body of the <code class="language-plaintext highlighter-rouge">autocomplete.html</code> page, create a template like this.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;script </span><span class="na">id=</span><span class="s">"autoCompleteItemTemplate"</span> <span class="na">type=</span><span class="s">"x-kendo-template"</span><span class="nt">&gt;</span>
  <span class="err">#</span> <span class="kd">var</span> <span class="nx">suggestionLabel</span> <span class="o">=</span> <span class="nx">address</span><span class="p">.</span><span class="nx">freeformAddress</span><span class="p">;</span> <span class="err">#</span>
  <span class="err">#</span> <span class="k">if</span> <span class="p">(</span><span class="nx">poi</span> <span class="o">&amp;&amp;</span> <span class="nx">poi</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span> <span class="err">#</span>
  <span class="err">#</span>   <span class="nx">suggestionLabel</span> <span class="o">=</span> <span class="nx">poi</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="dl">'</span><span class="s1"> - (</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">suggestionLabel</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">)</span><span class="dl">'</span><span class="p">;</span> <span class="err">#</span> 
  <span class="err">#</span> <span class="p">}</span> <span class="err">#</span>
  <span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span><span class="err">#</span><span class="p">:</span><span class="nx">suggestionLabel</span><span class="err">#</span><span class="o">&lt;</span><span class="sr">/span</span><span class="err">&gt;
</span><span class="nt">&lt;/script&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In line 1, we set the <code class="language-plaintext highlighter-rouge">id</code> of the script block to <code class="language-plaintext highlighter-rouge">autoCompleteItemTemplate</code>. This allows us to reference it from the AutoComplete control. We also set the <code class="language-plaintext highlighter-rouge">type</code> of the script to <code class="language-plaintext highlighter-rouge">x-kendo-template</code>. This helps with Intellisense.</p>

<p>In lines 2-6, we run some <em>logic</em> to create display text.  Line 2 creates a variable named <code class="language-plaintext highlighter-rouge">suggestionLabel</code> and sets it to the <code class="language-plaintext highlighter-rouge">address.freeformAddress</code> property.  Intellisense does not help a lot here, so watch your spelling. On line 3 we check to see if the property <code class="language-plaintext highlighter-rouge">poi</code> and <code class="language-plaintext highlighter-rouge">poi.name</code> are present, if they are person, on line 4 we set the <code class="language-plaintext highlighter-rouge">suggestionLabel</code> variable equal to <code class="language-plaintext highlighter-rouge">poi.name</code> +  - ( +  <code class="language-plaintext highlighter-rouge">suggestionLabel</code> + <code class="language-plaintext highlighter-rouge">)</code>.</p>

<p>Line 6 writes out a <code class="language-plaintext highlighter-rouge">span</code> element with the value of the <code class="language-plaintext highlighter-rouge">suggestionLabel</code> variable.</p>

<p>Based on the sample results above, we would get</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;span&gt;</span>Macy's Department Store - (151 W 34Th St, New York, NY 10001)<span class="nt">&lt;/span&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>They are written to the browser for this result item.</p>

<p>Now add the <code class="language-plaintext highlighter-rouge">template</code> property to the <code class="language-plaintext highlighter-rouge">queryText</code> initialization.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nx">template</span><span class="p">:</span> <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#autoCompleteItemTemplate</span><span class="dl">'</span><span class="p">).</span><span class="nx">html</span><span class="p">()</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Save the HTML and Javascript files.</p>

<h3 id="working-sample">Working sample</h3>

<p>If all is correct, when you refresh the page and type <code class="language-plaintext highlighter-rouge">Macy</code> in the text box, you should see something like this.</p>

<p><img src="/assets/images/posts/kendo-autocomplete-macy.png" alt="Search Suggestions for Macy" class="align-center" /></p>

<h2 id="creating-a-map-pin">Creating a Map Pin</h2>

<p>Now that we have the search suggestions displayed let’s center the map and add a pushpin with the search suggestion location.</p>

<p>Add the <code class="language-plaintext highlighter-rouge">select</code> function to the <code class="language-plaintext highlighter-rouge">queryText</code> element</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="nx">select</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">item</span> <span class="o">=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">dataItem</span><span class="p">;</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">item</span><span class="p">.</span><span class="nx">poi</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
    
    <span class="nx">map</span><span class="p">.</span><span class="nx">setCamera</span><span class="p">({</span>
        <span class="na">center</span><span class="p">:</span> <span class="p">[</span><span class="nx">item</span><span class="p">.</span><span class="nx">position</span><span class="p">.</span><span class="nx">lon</span><span class="p">,</span> <span class="nx">item</span><span class="p">.</span><span class="nx">position</span><span class="p">.</span><span class="nx">lat</span><span class="p">],</span>
        <span class="na">zoom</span><span class="p">:</span> <span class="nx">defaultZoom</span><span class="p">});</span>
    <span class="nx">azureMapDataSource</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="k">new</span> <span class="nx">atlas</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">Point</span><span class="p">([</span><span class="nx">item</span><span class="p">.</span><span class="nx">position</span><span class="p">.</span><span class="nx">lon</span><span class="p">,</span> <span class="nx">item</span><span class="p">.</span><span class="nx">position</span><span class="p">.</span><span class="nx">lat</span><span class="p">]));</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The Autocomplete widget passes an object to the function that is triggering the event.  In the <code class="language-plaintext highlighter-rouge">select</code> event, we need the <code class="language-plaintext highlighter-rouge">dataItem</code> property, a copy of the result above.</p>

<p>Line 2, we set a variable named <code class="language-plaintext highlighter-rouge">item</code> to the <code class="language-plaintext highlighter-rouge">dataItem</code> of the element.</p>

<p>Lines 5-7, we re-center the map to the new coordinates.  We use the <code class="language-plaintext highlighter-rouge">position</code> property to center the camera.</p>

<p>Line 8 draws a push pin at the coordinates of the selected item.</p>

<p>Save the files, refresh the browser, type <code class="language-plaintext highlighter-rouge">Macy</code>, and select “Macy’s Department Store”. If successful, the browser looks similar to this.</p>

<p><img src="/assets/images/posts/kendo-autocomplete-macy-selected.png" alt="Macy's Department store selected" class="align-center" /></p>

<h2 id="wrap-up">Wrap Up</h2>

<p>In about 100 lines of <em>our</em> code, we were able to use the Kendo UI Autocomplete control and the Azure Maps Search service to build an address/point of interest search component.</p>

<h2 id="completed-code">Completed Code</h2>

<p>The completed code for this post, less the <code class="language-plaintext highlighter-rouge">azurekey.js</code> file, can be found at <a href="https://github.com/jguadagno/kendo-autocomplete-azure-maps-search" target="_blank">https://github.com/jguadagno/kendo-autocomplete-azure-maps-search</a></p>

<h2 id="video">Video</h2>

<p>You can watch one of the sessions of `<a href="https://twitch.tv/jguadagno" target="_blank">Coding with JoeG</a>’ where I demonstrated this.</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/cmpzZQa76rs" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://docs.microsoft.com/en-us/azure/azure-maps/how-to-search-for-address?wt.mc_id=DT-MVP-4024623" target="_blank">Search for a location using Azure Maps Search service</a></li>
  <li><a href="https://www.telerik.com/kendo-ui" target="_blank">Kendo UI</a></li>
  <li><a href="https://demos.telerik.com/kendo-ui/autocomplete/index" target="_blank">Kendo UI Autocomplete Widget</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Azure Maps" /><category term="Azure Maps Search" /><category term="Telerik" /><category term="Kendo UI" /><category term="Autocomplete" /><summary type="html"><![CDATA[When using Google or Bing Maps to lookup an address or point of interest, both display address suggestions in a user-friendly way. In the example below, I type “Time Square”, and Bing suggests a few addresses/places.]]></summary></entry><entry><title type="html">Using Yarn with ASP.NET Core Projects</title><link href="https://www.josephguadagno.net/2021/01/18/using-yarn-with-asp-net-core-projects" rel="alternate" type="text/html" title="Using Yarn with ASP.NET Core Projects" /><published>2021-01-18T09:00:00-07:00</published><updated>2021-01-18T09:00:00-07:00</updated><id>https://www.josephguadagno.net/2021/01/18/using-yarn-with-asp-net-core-projects</id><content type="html" xml:base="https://www.josephguadagno.net/2021/01/18/using-yarn-with-asp-net-core-projects"><![CDATA[<p>Since I got bitten by the CI/CD bug, I’ve been looking at ways to clean up some code-bases and make it easier to deploy and store less <em>unneeded</em> files in my repositories.  This time around it got me looking into <a href="https://yarnpkg.com/" target="_blank">Yarn</a></p>

<blockquote>
  <p>Yarn is a package manager that doubles down as project manager. Whether you work on one-shot projects or large monorepos, as a hobbyist or an enterprise user, we’ve got you covered.</p>
</blockquote>

<p>If you are familiar with what Nuget is for packages in the .NET ecosystem, Yarn does the same thing except for web packages (HTML, CSS, javascript, etc).</p>

<p>I looked at Yarn to update the commonly used web frameworks like, <a href="https://www.getbootstrap.com" target="_blank">Bootstrap</a>, <a href="https://jquery.com" target="_blank">jQuery</a> (required for Bootstrap), and <a href="https://fontawesome.com/" target="_blank">Fontawesome</a> and I couldn’t find anything that told me how. Microsoft started a project called <a href="https://docs.microsoft.com/en-us/aspnet/core/client-side/libman/libman-vs?view=aspnetcore-5.0&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">LibMan</a> which helped with the management of packages but that only worked in the IDE.  So, as I like to do, I worked on figuring it out.</p>

<p>Let’s get to it!</p>

<h2 id="getting-started">Getting Started</h2>

<p>First, you need to down and install Yarn. Details can be found on their <a href="https://yarnpkg.com/getting-started" target="_blank">Getting Started</a> page. The second step, if you already have an existing ASP.NET project, Core/MVC/Razor, delete the <code class="language-plaintext highlighter-rouge">wwwroot\lib</code> folder, assuming you have nothing in it but the 3rd party packages.</p>

<h2 id="adding-packages-with-yarn">Adding Packages with Yarn</h2>

<h3 id="configuring-yarn">Configuring Yarn</h3>

<p>By default, Yarn will place everything in the <code class="language-plaintext highlighter-rouge">node_modules</code> folder which could work for you, for me, I was trying to have the smallest amount of changes to the rest of the project.  There are two ways to override that folder.  You can add <code class="language-plaintext highlighter-rouge">--modules-folder wwwroot/lib</code> to the command line every time you run Yarn, or you can create a Yarn configuration file.</p>

<p class="notice--info"><strong><em>NOTE</em></strong>: The <code class="language-plaintext highlighter-rouge">.yarnrc</code> file is going away, from what I can tell with v2 of Yarn.</p>

<p>To create the configuration file, you need to create a <code class="language-plaintext highlighter-rouge">.yarnrc</code> file in the root of your web project. Depending on what IDE you need, the instructions will vary slightly. Once the file is created, add the following line to the <code class="language-plaintext highlighter-rouge">.yarnrc</code> file. I found this on <a href="https://stackoverflow.com/a/53072639/89184" target="_blank">StackOverflow</a>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">--modules-folder</span> wwwroot/lib
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Save the file.</p>

<h3 id="adding-package">Adding Package</h3>

<p>I’m going to cover adding the required files for the templates that are included with ASP.NET.  Here, you will need to go to a command-line, terminal, bash-script, whichever is your choice, to add the packages.  The syntax is <code class="language-plaintext highlighter-rouge">yarn add &lt;packageName&gt;</code> where the <code class="language-plaintext highlighter-rouge">&lt;packageName&gt;</code> is the name of the package you want to add.</p>

<p>Here are the commands for the ASP.NET Core template.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre>yarn add popper.js <span class="o">(</span>deprecation warning<span class="o">)</span>
yarn add jquery
yarn add bootstrap
yarn add jquery-validation
yarn add jquery-validation-unobtrusive
yarn add @fortawesome/fontawesome-free
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--danger"><strong><em>Note</em></strong>: You will see a warning that <code class="language-plaintext highlighter-rouge">popper.js</code> is deprecated, you can ignore that, the next version of Bootstrap will have popper.js built-in so you will no longer need it.</p>

<p class="notice--info">If you have additional packages that you want to add, search the Yarn <a href="https://yarnpkg.com" target="_blank">site</a> to see what packages they have.  If you find the package there the site includes other details about the package, including the primary site for the package, what CDNs host the package, and more.</p>

<p>You’ll notice that a <code class="language-plaintext highlighter-rouge">package.json</code> and <code class="language-plaintext highlighter-rouge">yarn.lock</code> file gets created.  The <code class="language-plaintext highlighter-rouge">package.json</code> is the list. or manifest, of packages you have added. The files can be edited directly if you know the name and versions of the packages you want to install.  The <code class="language-plaintext highlighter-rouge">yarn.lock</code> file is used by Yarn and contains some more details around the packages.</p>

<p>If you used the location of <code class="language-plaintext highlighter-rouge">wwwroot/lib</code> you shouldn’t need to do anything else.  When you want to update a library, just edit the <code class="language-plaintext highlighter-rouge">package.json</code> file and run <code class="language-plaintext highlighter-rouge">yarn</code> from the terminal.</p>

<h2 id="using-a-content-delivery-network-cdn">Using a Content Delivery Network (CDN)</h2>

<p>Bonus Content! Win/Win!</p>

<p>What’s a content delivery network you ask?</p>

<blockquote>
  <p>A Content Delivery Network (CDN) is a system of geographically distributed servers that work together to provide fast delivery of Internet content. It’s designed to minimize latency in loading web pages by reducing the physical distance between the server and the user. A CDN allows for a quick transfer of assets needed to load content such as HTML pages, javascript files, stylesheets, images, and videos.</p>
</blockquote>

<p>Using Yarn to add your HTML, CSS, or javascript dependencies provides you with the option to not add these files to your source code repositories since you can install them at any time.  That choice is yours. However, using packages added by Yarn also make it easier for you to use CDNs for hosting these same files.</p>

<p>There are several CDNs out there to use. If you discover the package on the Yarn site, you’ll see mentions of a few CDNs like <a href="https://www.jsdelivr.com/" target="_blank">jsDeliver</a>, <a href="https://unpkg.com/" target="_blank">unpkg</a>, and <a href="https://bundle.run/" target="_blank">bundle.run</a>, I’ll use <a href="https://cdnjs.com" target="_blank">cdnjs</a> for these examples.</p>

<p>Using a CDN has several advantages but at the same time has a disadvantage. If the CDN you chose is down, your content will not be served which will lead to a less than designed look and feel for your site.  The good news is, ASP.NET has a built-in feature to fallback to local content in the event the CDN is down. These features are the <a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/script-tag-helper?view=aspnetcore-5.0&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Script</a> &amp; <a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/link-tag-helper?view=aspnetcore-5.0&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Link</a> tag helpers.</p>

<p>Looking at the sample for jQuery you’ll notice four additional attributes.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"</span>
  <span class="na">integrity=</span><span class="s">"sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="</span>
  <span class="na">crossorigin=</span><span class="s">"anonymous"</span>
  <span class="na">asp-fallback-src=</span><span class="s">"~/lib/jquery/dist/jquery.min.js"</span>
  <span class="na">asp-fallback-test=</span><span class="s">"window.jQuery"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/script&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Usage</td>
    </tr>
    <tr>
      <td>asp-fallback-src</td>
      <td>The local source to use if the CDN failed to load the resource</td>
    </tr>
    <tr>
      <td>asp-fallback-test</td>
      <td>A test that ASP.NET will inject into your page to see if loading from the CDN worked.</td>
    </tr>
  </tbody>
</table>

<p>You can replace all of the scripts now with a <code class="language-plaintext highlighter-rouge">script</code> tag similar to this one.  Later in the post, you’ll see a working example.</p>

<h2 id="using-the-environment-tag-helper">Using the Environment Tag Helper</h2>

<p>Even more bonus content! Win/Win Again!</p>

<p>ASP.NET Core added an <a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/environment-tag-helper?view=aspnetcore-5.0&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Environment</a> tag helper.  This helper allows you to render content specific to an environment.  This means, you can scripts from your local machine when running in development and serve them from a CDN when running in production. I know, at this point you are probably saying <em>‘Joe, that sounds really complicated’</em>.  Well, it isn’t! Let me show you.</p>

<p>In this example.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;environment</span> <span class="na">include=</span><span class="s">"Staging,Production"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;strong&gt;</span>IWebHostEnvironment.EnvironmentName is Staging or Production<span class="nt">&lt;/strong&gt;</span>
<span class="nt">&lt;/environment&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>ASP.NET will render <strong>IWebHostEnvironment.EnvironmentName is Staging or Production</strong>.</p>

<p>You can use the <code class="language-plaintext highlighter-rouge">exclude</code> and <code class="language-plaintext highlighter-rouge">include</code> attributes together to create something like this.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;environment</span> <span class="na">include=</span><span class="s">"Development"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"~/lib/bootstrap/dist/css/bootstrap.min.css"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"~/css/site.css"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/environment&gt;</span>
<span class="nt">&lt;environment</span> <span class="na">exclude=</span><span class="s">"Development"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/css/bootstrap.min.css"</span>
          <span class="na">integrity=</span><span class="s">"sha512-oc9+XSs1H243/FRN9Rw62Fn8EtxjEYWHXRvjS43YtueEewbS6ObfXcJNyohjHqVKFPoXXUxwc+q1K7Dee6vv9g=="</span>
          <span class="na">crossorigin=</span><span class="s">"anonymous"</span>
          <span class="na">asp-fallback-href=</span><span class="s">"~/lib/bootstrap/dist/css/bootstrap.min.css"</span>
          <span class="na">asp-fallback-test-class=</span><span class="s">"sr-only"</span> <span class="na">asp-fallback-test-property=</span><span class="s">"position"</span> <span class="na">asp-fallback-test-value=</span><span class="s">"absolute"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"~/css/site.css"</span> <span class="na">asp-append-version=</span><span class="s">"true"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/environment&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In the <code class="language-plaintext highlighter-rouge">Development</code> environment, ASP.NET Core will render the files from the local machine. In all other environments, it will try from a CDN first, then locally.</p>

<h3 id="new-template">New Template</h3>

<p>Here is a completed template using the <code class="language-plaintext highlighter-rouge">Environment</code> tags, and CDNs.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
</pre></td><td class="rouge-code"><pre><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span>
<span class="nt">&lt;head&gt;</span>
    <span class="nt">&lt;meta</span> <span class="na">charset=</span><span class="s">"utf-8"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1.0"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;title&gt;</span>@ViewData["Title"] - @conferenceName<span class="nt">&lt;/title&gt;</span>
    <span class="nt">&lt;environment</span> <span class="na">include=</span><span class="s">"Development"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"~/lib/bootstrap/dist/css/bootstrap.min.css"</span><span class="nt">/&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"~/css/site.css"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/environment&gt;</span>
    <span class="nt">&lt;environment</span> <span class="na">exclude=</span><span class="s">"Development"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/css/bootstrap.min.css"</span>
              <span class="na">integrity=</span><span class="s">"sha512-oc9+XSs1H243/FRN9Rw62Fn8EtxjEYWHXRvjS43YtueEewbS6ObfXcJNyohjHqVKFPoXXUxwc+q1K7Dee6vv9g=="</span>
              <span class="na">crossorigin=</span><span class="s">"anonymous"</span>
              <span class="na">asp-fallback-href=</span><span class="s">"~/lib/bootstrap/dist/css/bootstrap.min.css"</span>
              <span class="na">asp-fallback-test-class=</span><span class="s">"sr-only"</span> <span class="na">asp-fallback-test-property=</span><span class="s">"position"</span> <span class="na">asp-fallback-test-value=</span><span class="s">"absolute"</span><span class="nt">/&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"~/css/site.css"</span> <span class="na">asp-append-version=</span><span class="s">"true"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/environment&gt;</span>
    <span class="nt">&lt;environment</span> <span class="na">include=</span><span class="s">"Development"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;script </span><span class="na">defer</span> <span class="na">src=</span><span class="s">"~/lib/@("</span><span class="err">@")</span><span class="na">fortawesome</span><span class="err">/</span><span class="na">fontawesome-free</span><span class="err">/</span><span class="na">js</span><span class="err">/</span><span class="na">all.js</span><span class="err">"</span><span class="nt">&gt;&lt;/script&gt;</span>
    <span class="nt">&lt;/environment&gt;</span>
    <span class="nt">&lt;environment</span> <span class="na">exclude=</span><span class="s">"Development"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/js/all.min.js"</span>
                <span class="na">integrity=</span><span class="s">"sha512-F5QTlBqZlvuBEs9LQPqc1iZv2UMxcVXezbHzomzS6Df4MZMClge/8+gXrKw2fl5ysdk4rWjR0vKS7NNkfymaBQ=="</span>
                <span class="na">crossorigin=</span><span class="s">"anonymous"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;/script&gt;</span>
    <span class="nt">&lt;/environment&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
<span class="nt">&lt;header&gt;</span>
    <span class="nt">&lt;nav</span> <span class="na">class=</span><span class="s">"navbar navbar-expand-sm navbar-toggleable-sm navbar-dark bg-dark border-bottom box-shadow mb-3"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"navbar-brand"</span> <span class="na">asp-area=</span><span class="s">""</span> <span class="na">asp-controller=</span><span class="s">"Home"</span> <span class="na">asp-action=</span><span class="s">"Index"</span><span class="nt">&gt;</span>Home Page<span class="nt">&lt;/a&gt;</span>
            <span class="nt">&lt;button</span> <span class="na">class=</span><span class="s">"navbar-toggler"</span> <span class="na">type=</span><span class="s">"button"</span> <span class="na">data-toggle=</span><span class="s">"collapse"</span> <span class="na">data-target=</span><span class="s">".navbar-collapse"</span> <span class="na">aria-controls=</span><span class="s">"navbarSupportedContent"</span>
                    <span class="na">aria-expanded=</span><span class="s">"false"</span> <span class="na">aria-label=</span><span class="s">"Toggle navigation"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"navbar-toggler-icon"</span><span class="nt">&gt;&lt;/span&gt;</span>
            <span class="nt">&lt;/button&gt;</span>
            <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"navbar-collapse collapse d-sm-inline-flex justify-content-between"</span><span class="nt">&gt;</span>
                <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">"navbar-nav flex-grow-1"</span><span class="nt">&gt;</span>
                    <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span>
                        <span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"nav-link"</span> <span class="na">asp-area=</span><span class="s">""</span> <span class="na">asp-controller=</span><span class="s">"Home"</span> <span class="na">asp-action=</span><span class="s">"Index"</span><span class="nt">&gt;</span>Home<span class="nt">&lt;/a&gt;</span>
                    <span class="nt">&lt;/li&gt;</span>
                    <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span>
                        <span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"nav-link"</span> <span class="na">asp-area=</span><span class="s">""</span> <span class="na">asp-controller=</span><span class="s">"Events"</span> <span class="na">asp-action=</span><span class="s">"About"</span><span class="nt">&gt;</span>About<span class="nt">&lt;/a&gt;</span>
                    <span class="nt">&lt;/li&gt;</span>
                <span class="nt">&lt;/ul&gt;</span>
            <span class="nt">&lt;/div&gt;</span>
        <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;/nav&gt;</span>
<span class="nt">&lt;/header&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;main</span> <span class="na">role=</span><span class="s">"main"</span> <span class="na">class=</span><span class="s">"pb-3"</span><span class="nt">&gt;</span>
        @RenderBody()
    <span class="nt">&lt;/main&gt;</span>
<span class="nt">&lt;/div&gt;</span>

<span class="nt">&lt;footer</span> <span class="na">class=</span><span class="s">"border-top footer text-muted"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;</span>
        <span class="ni">&amp;copy;</span> 2021 - JosephGuadagno.NET, LLC - <span class="nt">&lt;a</span> <span class="na">asp-area=</span><span class="s">""</span> <span class="na">asp-controller=</span><span class="s">"Home"</span> <span class="na">asp-action=</span><span class="s">"Privacy"</span><span class="nt">&gt;</span>Privacy<span class="nt">&lt;/a&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/footer&gt;</span>
<span class="nt">&lt;environment</span> <span class="na">include=</span><span class="s">"Development"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"~/lib/jquery/dist/jquery.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
    <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;/environment&gt;</span>
<span class="nt">&lt;environment</span> <span class="na">exclude=</span><span class="s">"Development"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"</span>
            <span class="na">integrity=</span><span class="s">"sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="</span>
            <span class="na">crossorigin=</span><span class="s">"anonymous"</span>
            <span class="na">asp-fallback-src=</span><span class="s">"~/lib/jquery/dist/jquery.min.js"</span>
            <span class="na">asp-fallback-test=</span><span class="s">"window.jQuery"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;/script&gt;</span>

    <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.3/js/bootstrap.min.js"</span>
            <span class="na">integrity=</span><span class="s">"sha512-8qmis31OQi6hIRgvkht0s6mCOittjMa9GMqtK9hes5iEQBQE/Ca6yGE5FsW36vyipGoWQswBj/QBm2JR086Rkw=="</span>
            <span class="na">crossorigin=</span><span class="s">"anonymous"</span>
            <span class="na">asp-fallback-src=</span><span class="s">"~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"</span>
            <span class="na">asp-fallback-test=</span><span class="s">"window.jQuery &amp;&amp; window.jQuery.fn &amp;&amp; window.jQuery.fn.modal"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/environment&gt;</span>

<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"~/js/site.js"</span> <span class="na">asp-append-version=</span><span class="s">"true"</span><span class="nt">&gt;&lt;/script&gt;</span>
@await RenderSectionAsync("Scripts", required: false)
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="wrapping-up">Wrapping Up</h2>

<p>I hope this helps you.  In a future post, I’ll add how I can use Yarn in my build scripts so I don’t have to check in all of the package contents.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Yarn" /><category term="ASP.NET" /><category term="ASP.NET Core" /><category term="ASP.NET Core MVC" /><category term="Razor" /><category term="MVC" /><category term="Web" /><summary type="html"><![CDATA[Since I got bitten by the CI/CD bug, I’ve been looking at ways to clean up some code-bases and make it easier to deploy and store less unneeded files in my repositories. This time around it got me looking into Yarn]]></summary></entry><entry><title type="html">Hard Wire your Home without Ethernet using MoCA</title><link href="https://www.josephguadagno.net/2020/12/26/hard-wire-your-home-without-using-ethernet-using-moca" rel="alternate" type="text/html" title="Hard Wire your Home without Ethernet using MoCA" /><published>2020-12-26T19:20:00-07:00</published><updated>2020-12-28T10:55:00-07:00</updated><id>https://www.josephguadagno.net/2020/12/26/hard-wire-your-home-without-using-ethernet-using-moca</id><content type="html" xml:base="https://www.josephguadagno.net/2020/12/26/hard-wire-your-home-without-using-ethernet-using-moca"><![CDATA[<p>There are so many devices that are connected to our home networks nowadays. I have multiple Amazon Fire Sticks, computers, smart TVs, mobile phones, smart appliances, and so much more.  At any given time, there are around forty devices connected to my home network. Since working from home, I have three to four machines that are constantly ‘on the internet’.  The main house TV, my work computer, my son’s computer or Xbox, and the daughter’s Fire Stick/YouTube.  These devices are typically streaming in some way, shape, or form.  All of these devices, plus the other 30 or so on, were taxing my router. I currently don’t have any CAT5/6/7 wiring in my house, so I tried extending my wireless network a few different ways.</p>

<h2 id="the-attempts">The Attempts</h2>

<p>On the first attempt, I extended my wireless network with a network repeater.  I already had a <a href="https://amzn.to/3rw7NI0" target="_blank">NETGEAR Nighthawk X4S WiFi Router (R7800)</a>, so I added a <a href="https://amzn.to/3aOctTt">NETGEAR WiFi Mesh Range Extender (EX8000)</a> to offload some of the wireless clients too. This combination had ‘FastLane3’ technology (Dedicated WiFi Link to the router to avoid halving bandwidth of extended WiFi signals and innovative antenna design for ultimate coverage). This attempt worked for a bit except when I was on a video call, the wife was streaming, and the son was gaming.  There were packet drops, and the latency was spiking.  This did not work for us. So I looked into other solutions.  I wanted to get an ethernet connection in my son’s office to reduce or eliminate the network latency and packet drops.</p>

<p>This led to attempt two to resolve the network latency and packet drops.  I updated my router to <a href="https://amzn.to/3BVGw7G" target="_blank">ASUS ROG Rapture WiFi 6 Gaming Router (GT-AX11000) - Tri-Band 10 Gigabit Wireless Router</a> This router has all kinds of benefits like three bands, so I could dedicate a separate band to my son’s office (his computer and Xbox). This router also can add nodes to the network to create a mesh network using its AIMesh.  I tried just using this device by itself for a few days, and the problems did not go away. I then proceeded to add the NETGEAR EX8000 repeater to the network to extend the network, and after a few days, the network latency and packet drops started again.</p>

<p class="notice--info">Update September 30th, 2022</p>
<p>I decided to try out the AI Mesh feature of the ASUS ROG Rapture router by adding another AIMesh capable router to the mix. I picked up a <a href="https://amzn.to/3y4duSn" target="_blank">ASUS AX1800 WiFi 6 Router (RT-AX1800S) – Dual Band Gigabit AX Wireless Internet Router</a> router and added it to the network.  Now I have additional bandwidth and network slots upstairs without drilling any holes or running any wires.  I have been using this setup for a few months now, and I have not had any network latency or packet drops.  I am very happy with this setup.</p>

<h2 id="multimedia-over-coax-alliance">Multimedia over Coax Alliance</h2>

<p>This led me to research other options. That is when I discovered MoCA. MoCA (stands for <a href="https://en.wikipedia.org/wiki/Multimedia_over_Coax_Alliance" target="_blank">Multimedia over Coax Alliance</a>) is a technology that uses the existing coaxial cables already in most people’s homes. In essence, MoCA creates a wired Internet home network without the headache of drilling holes or running wires. Because MoCA technology is wired, it also delivers a reliable, low-lag, and ultra-high-speed connection. All of these are critical to a good streaming video or online gaming experience.</p>

<p><img src="/assets/images/posts/moca-example.jpg" alt="Sample MoCA network" class="align-center" /></p>

<p>After watching this <a href="https://youtu.be/HYya7RrQuJU" target="_blank">video</a> and checking out a few diagrams like this one, <a href="https://image.ibb.co/jSsMmT/layout.png">diagram 1</a>, I decided to give this a shot since I have an existing unused Coax network in my home.  I purchased 2 <a href="https://amzn.to/34OHKlA" target="_blank">goCoax MoCA 2.5 Adapter for Ethernet Over Coax</a> adapters to give it a shot.</p>

<h2 id="setup">Setup</h2>

<p class="notice--info"><strong>Note</strong> You need at least 2 MoCA adapters for this work. Also, MoCA can support a maximum of 16 adapters on one network.</p>

<p>My setup is based on having the goCoax adapters.  You will need at least 2 coax cables for this setup.  If you are using another MoCA adapter that is not the goCoax one, you may need to purchase <a href="https://amzn.to/2WPmUxP" target="_blank">cable splitters</a>.  The goCoax adapters come with the splitter built-in.</p>

<p>The setup was easy and will vary depending on your home network and internet access. For me, I have the local cable company provide Internet only and through coax. I have the cable modem in my office, which is connected via coax.</p>

<p>Step 1: Disconnect the coax cable from the cable modem.</p>

<p>Step 2: Connect the cable connected to the cable modem to the MOCA port on the goCoax.</p>

<p>Step 3: Connect a new coax cable to the TV port of the goCoax adapter and connect the other end of the cable to your cable modem.</p>

<p>Step 4: Power the goCoax device.</p>

<p>Step 5: Connect an ethernet cable from the LAN port of the goCoax device.</p>

<p>At this point, the ‘network traffic’ is going to be sent over the coax network.  If you want a hard-wired network connection in another location of the house, as long as there is a coax port nearby, you can install another goCoax adapter, and you should be good.</p>

<p>A visual of the setup.</p>

<p><img src="/assets/images/posts/moca-sample-setup.jpg" alt="MoCA network setup" class="align-center" /></p>

<h2 id="wrap-up">Wrap up</h2>

<p>After a week of using this set up, there have been no network drops, no latency issue, and virtually no pack loss.</p>

<p>In short, if your wireless network is struggling, you don’t have ethernet wiring through out your home, and you have coax wiring, MoCA adapters are a low-cost way to help.</p>

<h2 id="security-matters">Security Matters</h2>

<p>If you install a MoCA setup, you should check to see if you have a ‘Point of Entry’ (POE) filter installed where your network connection comes into your home. They look something like this.</p>

<p><img src="/assets/images/posts/moca-poe.jpg" alt="MoCA POE" class="align-center" /></p>

<p>A POE prevents interference between subscriber homes that use MoCA technology. But more importantly, it prevents you from leaking your network data back to your provider or anyone/anything else sharing the cable wiring. You can get on <a href="https://amzn.to/2JqWGij" target="_blank">Amazon</a> for under $10.</p>

<p class="notice"><strong>Note</strong> If you click on an Amazon link and purchase a product, I may get a commission from Amazon. The purpose of the links is to avoid searching and not make money on the blog post.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="MoCa" /><category term="Networking" /><summary type="html"><![CDATA[There are so many devices that are connected to our home networks nowadays. I have multiple Amazon Fire Sticks, computers, smart TVs, mobile phones, smart appliances, and so much more. At any given time, there are around forty devices connected to my home network. Since working from home, I have three to four machines that are constantly ‘on the internet’. The main house TV, my work computer, my son’s computer or Xbox, and the daughter’s Fire Stick/YouTube. These devices are typically streaming in some way, shape, or form. All of these devices, plus the other 30 or so on, were taxing my router. I currently don’t have any CAT5/6/7 wiring in my house, so I tried extending my wireless network a few different ways.]]></summary></entry><entry><title type="html">Coding with JoeG - Streaming Software Configuration - December 2020 Update</title><link href="https://www.josephguadagno.net/2020/12/25/coding-with-joeg-streaming-software-configuration-december-2020-update" rel="alternate" type="text/html" title="Coding with JoeG - Streaming Software Configuration - December 2020 Update" /><published>2020-12-25T09:30:00-07:00</published><updated>2020-12-25T09:30:00-07:00</updated><id>https://www.josephguadagno.net/2020/12/25/coding-with-joeg-streaming-software-configuration-december-2020-update</id><content type="html" xml:base="https://www.josephguadagno.net/2020/12/25/coding-with-joeg-streaming-software-configuration-december-2020-update"><![CDATA[<p>In a previous post, I covered the <a href="/2020/12/20/coding-with-joeg-streaming-hardware-configuration-december-2020-update">hardware</a> I use for the stream.  In this post, I’ll cover the software and settings to ‘produce’ the stream and artifacts. Please keep in mind, this is a guide based on some of my searchings, my playing around, and my hardware, your needs may be different. Another thing to keep in mind, is these are my settings for streaming live coding not live gaming.</p>

<h2 id="streamlabs-obs-configuration">Streamlabs OBS Configuration</h2>

<p>Streamlabs OBS is a free reliable streaming app with the fastest setup process on the market. We have developed an all-in-one application making streaming easy for everyone. Whether you’re a novice or experienced streamer, Streamlabs OBS will provide you the best streaming experience, with tools built to engage, grow, and monetize your channel.</p>

<p><a href="https://streamlabs.com/streamlabs-obs">Download</a></p>

<h3 id="settings">Settings</h3>

<p>The first thing, from what I remember, is that Streamlabs OBS runs an ‘Auto Optimize’ function, which you can find on the general tab of settings.  From everything I have seen and read, you really never want to, except for maybe the first time.  Most of the time, you will want to ‘tweak’ the settings to fit your needs.  I’ve been on streams where the host says “What a minute. I have to tweak something”.  It’s gonna happen for a while.</p>

<p>First, we are going to talk about the setting for Streamlabs OBS.  You can get to these by clicking on gear (<i class="fas fa-cog"></i>) icon. Again, some settings might not be applicable depending on your hardware.</p>

<h4 id="general-tab">General Tab</h4>

<p>Everything in this tab is according to your preference.  For me, everything is unchecked except for:</p>

<ul>
  <li>Confirm stream title and game before going live</li>
  <li>In the ‘Output’ section, Automatically record when streaming</li>
  <li>In the ‘Source Alignment Snapping’, all 3 checked.</li>
</ul>

<h4 id="stream-tab">Stream Tab</h4>

<p>Enable multistream, if you are.  For me, as the writing of this post, I’m only live streaming to <a href="https://twitch.tv/jguadagno" target="_blank">Twitch</a></p>

<h4 id="output-tab">Output Tab</h4>

<p>For this tab, I have a few things tweaked based on my hardware and conditions.  Please select the <code class="language-plaintext highlighter-rouge">Advanced</code> option for ‘Output mode’.</p>

<h5 id="output---streaming">Output - Streaming</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Audio Track</td>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
      <td>Only have one track.  This is helpful if you want to separate music from your voice</td>
    </tr>
    <tr>
      <td>Encoder</td>
      <td><code class="language-plaintext highlighter-rouge">Hardware (NVENC)</code></td>
      <td>This is set to software, by default, I changed it to hardware because I wanted to offload some of the video encoding to my video chip/card since my PC not fast enough</td>
    </tr>
    <tr>
      <td>Enforce stream service encoder settings</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Rate Control</td>
      <td><code class="language-plaintext highlighter-rouge">CBR</code></td>
      <td><em>Constant Bitrate Stream</em>: This is the standard bitrate to use for streamers. Let’s say you’ve set your video bitrate to 3000 Kbps for your next stream, using CBR means your stream will always be at 3000 Kbps, even when less could be used, such as a dark game that lacks detail.</td>
    </tr>
    <tr>
      <td>Bit Rate</td>
      <td><code class="language-plaintext highlighter-rouge">6000</code></td>
      <td>The higher the better</td>
    </tr>
    <tr>
      <td>Keyframe Interval</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td><code class="language-plaintext highlighter-rouge">0</code> Indicates that Streamlabs will figure it out before it starts. The “Keyframe Interval” could be set at 2, meaning that the video frame will be rendered every 2 seconds.</td>
    </tr>
    <tr>
      <td>Preset</td>
      <td><code class="language-plaintext highlighter-rouge">Performance</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Profile</td>
      <td><code class="language-plaintext highlighter-rouge">high</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Look-ahead</td>
      <td><code class="language-plaintext highlighter-rouge">Unchecked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Psycho Visual Tuning</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>GPU</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Max B-frames</td>
      <td><code class="language-plaintext highlighter-rouge">2</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>For a more in-depth look at bitrates, and keyframes, check out <a href="https://castr.io/blog/what-is-video-bitrate/" target="_blank">What is Video Bitrate</a>.</p>

<h5 id="output---recording">Output - Recording</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Type</td>
      <td><code class="language-plaintext highlighter-rouge">Standard</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Recording Path</td>
      <td><em>local storage</em></td>
      <td>If you can, this should be a SSD on a different partition/device than your operating system.  Avoid recording to a network drive</td>
    </tr>
    <tr>
      <td>Generate File Name without Space</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Recording Format</td>
      <td><code class="language-plaintext highlighter-rouge">mp4</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Audio Track</td>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
      <td>Only have one track.  This is helpful if you want to separate music from your voice</td>
    </tr>
    <tr>
      <td>Encoder</td>
      <td><code class="language-plaintext highlighter-rouge">Hardware (NVENC)</code></td>
      <td>This is set to software, by default, I changed it to hardware because I wanted to offload some of the video encoding to my video chip/card since my PC has a detected video card now.</td>
    </tr>
    <tr>
      <td>Enforce stream service encoder settings</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Rate Control</td>
      <td><code class="language-plaintext highlighter-rouge">CBR</code></td>
      <td><em>Constant Bitrate Stream</em>: This is the standard bitrate to use for streamers. Let’s say you’ve set your video bitrate to 3000 Kbps for your next stream, using CBR means your stream will always be at 3000 Kbps, even when less could be used, such as a dark game that lacks detail.</td>
    </tr>
    <tr>
      <td>Bit Rate</td>
      <td><code class="language-plaintext highlighter-rouge">6000</code></td>
      <td>The higher the better</td>
    </tr>
    <tr>
      <td>Keyframe Interval</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td><code class="language-plaintext highlighter-rouge">0</code> Indicates that Streamlabs will figure it out before it starts. The “Keyframe Interval” could be set at 2, meaning that the video frame will be rendered every 2 seconds.</td>
    </tr>
    <tr>
      <td>Preset</td>
      <td><code class="language-plaintext highlighter-rouge">Performance</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Profile</td>
      <td><code class="language-plaintext highlighter-rouge">high</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Look-ahead</td>
      <td><code class="language-plaintext highlighter-rouge">Unchecked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Psycho Visual Tuning</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>GPU</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Max B-frames</td>
      <td><code class="language-plaintext highlighter-rouge">2</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="output---audio">Output - Audio</h5>

<p>All of these settings are untouched</p>

<h5 id="output---replay-buffer">Output - Replay Buffer</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enabled Replay Buffer</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td>This was on by default, I have not changed it</td>
    </tr>
    <tr>
      <td>Maximum Replay Time (Seconds)</td>
      <td><code class="language-plaintext highlighter-rouge">20</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h4 id="audio">Audio</h4>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Sample Rate</td>
      <td><code class="language-plaintext highlighter-rouge">48khz</code></td>
      <td>Set this to the highest value your audio will support! The higher the hertz, the better the audio quality</td>
    </tr>
    <tr>
      <td>Channels</td>
      <td><code class="language-plaintext highlighter-rouge">Stereo</code></td>
      <td>Especially if you have music playing</td>
    </tr>
    <tr>
      <td>Desktop Audio Device 1</td>
      <td><code class="language-plaintext highlighter-rouge">Disabled</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Desktop Audio Device 2</td>
      <td><code class="language-plaintext highlighter-rouge">Disabled</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Mic/Auxiliary Device 1</td>
      <td><code class="language-plaintext highlighter-rouge">Disabled</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Mic/Auxiliary Device 2</td>
      <td><code class="language-plaintext highlighter-rouge">Disabled</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Mic/Auxiliary Device 3</td>
      <td><code class="language-plaintext highlighter-rouge">Disabled</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>You can select your microphone for either of the <em>mic/auxiliary</em> slots if you chose to. Doing so will make your microphone available to all scenes without adding it.</p>

<h4 id="video">Video</h4>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Base (Canvas) Resolution</td>
      <td><code class="language-plaintext highlighter-rouge">1920x1080</code></td>
      <td><strong>1080p</strong>. Most providers support 1080p and 4k.  If you stream in 4k the resolution on the viewer’s screen will be different if they don’t have a 4k monitor due to scaling.</td>
    </tr>
    <tr>
      <td>Output (Scaled) Resolution</td>
      <td><code class="language-plaintext highlighter-rouge">19020x1080</code></td>
      <td><strong>HD</strong></td>
    </tr>
    <tr>
      <td>Downscale Filter</td>
      <td><code class="language-plaintext highlighter-rouge">Bicubic (Sharpened scaling, 16 samples)</code></td>
      <td><strong>Bilateral</strong>, best for CPU. <strong>Lanczos</strong>, is better quality but CPU intensive. <strong>Bicubic</strong>, is the in-between.</td>
    </tr>
    <tr>
      <td>FPS Type</td>
      <td><code class="language-plaintext highlighter-rouge">Common FPS Values</code></td>
      <td>This will defaults to Frames per Second based on the resolution</td>
    </tr>
    <tr>
      <td>Common FPS Values</td>
      <td><code class="language-plaintext highlighter-rouge">60</code></td>
      <td>You can lower this to 30 if you are seeing an issue with your CPU. For streaming code, 30 is an acceptable value.</td>
    </tr>
  </tbody>
</table>

<h4 id="advanced">Advanced</h4>

<h5 id="advanced---general">Advanced - General</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Process Priority</td>
      <td><code class="language-plaintext highlighter-rouge">High</code></td>
      <td>I have this set to high because I am using a separate machine for streaming otherwise you should probably chose <code class="language-plaintext highlighter-rouge">normal</code></td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---video">Advanced - Video</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Color Format</td>
      <td><code class="language-plaintext highlighter-rouge">NV12</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>YUV Color Space</td>
      <td><code class="language-plaintext highlighter-rouge">601</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>YUV Color Range</td>
      <td><code class="language-plaintext highlighter-rouge">Partial</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Force CPU as render device</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td>I wanted to offload the encoding of video to the video chip</td>
    </tr>
  </tbody>
</table>

<p>Some of your default settings for video might be different depending on your locale.</p>

<h5 id="advanced---audio">Advanced - Audio</h5>

<p>I do not have a separate device to ‘monitor’ the stream audio</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Audio Monitoring Device</td>
      <td><code class="language-plaintext highlighter-rouge">Default</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Disable Windows audio ducking</td>
      <td><code class="language-plaintext highlighter-rouge">unchecked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---recording">Advanced - Recording</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Filename Formatting</td>
      <td><code class="language-plaintext highlighter-rouge">%CCYY-%MM-%DD %hh-%mm-%ss</code></td>
      <td>This is the default value. I kept it because I keep all of the recordings… so far.</td>
    </tr>
    <tr>
      <td>Overwrite if file exists</td>
      <td><code class="language-plaintext highlighter-rouge">unchecked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---replay-buffer">Advanced - Replay Buffer</h5>

<p>These are the default values.  I imagine that if you are streaming your gaming, you might want to provide some replay functionality for a ‘sweet kill’.  No one said ever, ‘That was a great use of a lambda, let me replay that’ :smile:</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Replay Buffer Filename Prefix</td>
      <td><code class="language-plaintext highlighter-rouge">Replay</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Replay Buffer Filename Suffix</td>
      <td><em>empty</em></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---stream-delay">Advanced - Stream Delay</h5>

<p>These are the default values.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enable</td>
      <td><code class="language-plaintext highlighter-rouge">Unchecked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Duration</td>
      <td><code class="language-plaintext highlighter-rouge">20</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Preserve cutoff point (increase delay) when reconnection</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---automatically-reconnect">Advanced - Automatically Reconnect</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enable</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Retry Delay (seconds)</td>
      <td><code class="language-plaintext highlighter-rouge">10</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Maximum Retries</td>
      <td><code class="language-plaintext highlighter-rouge">20</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---network">Advanced - Network</h5>

<p>If you have more than one Network Interface Card (nic), exampled wired and wireless. You can set Streamlabs OBS to only use one.  If you have a wired connection, you should use that.  Wireless can drop packets.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Bind to IP</td>
      <td><code class="language-plaintext highlighter-rouge">Default</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Dynamically change bitrate when dropping frames while streaming</td>
      <td><code class="language-plaintext highlighter-rouge">unchecked</code></td>
      <td>If you notice your CPU or internet connection can not handle the load you may want this checked.  Doing so, will drop the frame rate , the number of ‘snapshots’ that the view sees, which could degrade quality.  If you are later posting the recording, I would keep this unchecked.</td>
    </tr>
    <tr>
      <td>Enable new networking code</td>
      <td><code class="language-plaintext highlighter-rouge">unchecked</code></td>
      <td><a href="https://www.reddit.com/r/Twitch/comments/8kwue6/obs_new_network_code_option/">Explanation</a></td>
    </tr>
    <tr>
      <td>Low latency mode</td>
      <td><code class="language-plaintext highlighter-rouge">Unchecked</code></td>
      <td>This should be checked if you are seeing a lot of dropped frames or low/show upload bandwidth from your ISP.</td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---sources">Advanced - Sources</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enable Browser Source Hardware Acceleration</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---media-files">Advanced - Media Files</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enable media file caching</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h4 id="other-settings">Other settings</h4>

<p>All of the other settings, Hotkeys, Game Overlay, Scene Collections, Notifications, Appearance, and Face Masks, I have left the default values.</p>

<h3 id="scenes">Scenes</h3>

<p>Scenes in OBS provide a different way to provide content to the viewer. Do you just want to show code? Do you just want to show you?  Scenes are a way to do that, and a preference.  From what I have seen, no two streams are the same.  I have eight scenes registered, three of them are just helper scenes that are reused as part of other scenes.</p>

<p><img src="/assets/images/posts/streaming-software-scenes.png" alt="Coding with JoeG Scenes" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Scene Name</td>
      <td>Use</td>
    </tr>
    <tr>
      <td>Overlay</td>
      <td>Samples, provided by the theme, with some visual UI elements</td>
    </tr>
    <tr>
      <td>Starting Scene</td>
      <td>I use this when I go live, to let the viewers know “I’ll be there in a minute”</td>
    </tr>
    <tr>
      <td>Live Share - Elgato</td>
      <td>This is my primary scene. It shares the primary capture from the Elgato card with the video from the webcam</td>
    </tr>
    <tr>
      <td>Be Right Back Scene</td>
      <td>I have not used this one yet.  It’s for the times, I might need to run and get the door, or another drink, or something</td>
    </tr>
    <tr>
      <td>Ending Scene</td>
      <td>This signals the users that the stream is ending and provides some social media links for them to continue the conversation.  My mic is still on at this point.  This might be replaced with the <em>Just Chatting</em> scene which I am still building.</td>
    </tr>
    <tr>
      <td>Just Chatting</td>
      <td>Work in progress.  This will just be me with some background images for just the conversation.</td>
    </tr>
    <tr>
      <td>Secret Time</td>
      <td>I use this when I am working with secrets on screen and I don’t want to share them :smile:</td>
    </tr>
    <tr>
      <td>Social Media</td>
      <td>A shared scene with all of my social media links</td>
    </tr>
    <tr>
      <td>Alerts</td>
      <td>A shared scene with the OBS/Twitch alerts for new followers, subscribers, etc.</td>
    </tr>
  </tbody>
</table>

<p>All of my scenes and alerts use the <a href="https://www.own3d.tv/product/pure-series-package/" target="_blank">Pure</a> theme by <a href="https://www.own3d.tv" target="_blank">Own3d</a> for a consistent look.</p>

<h3 id="filters">Filters</h3>

<p>Filters are like advanced settings that let you tweak your devices (sources) even more.  I’m going to cover some of the Webcam and Microphone filters I use to provide a better experience.</p>

<p>One thing to keep in mind is that the order of the filters matter.  The output of one filter is feed in as the source of the next filter.  So if you have four filters, the input of filter 4 would be the output of the third, not the source data. So, you might want to play around with the ordering a bit.</p>

<p>To get to the filters in OBS, right-click on a source and chose <code class="language-plaintext highlighter-rouge">Filters</code>.</p>

<h4 id="webcam-filters">Webcam Filters</h4>

<h5 id="webcam-filter---croppad">Webcam Filter - Crop/Pad</h5>

<p>This filter will remove or add pixels to your webcam.  The webcam I use has a wide range, so I crop out a lot of it just so my face and shoulders are available.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Left</td>
      <td><code class="language-plaintext highlighter-rouge">300</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Top</td>
      <td><code class="language-plaintext highlighter-rouge">180</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Right</td>
      <td><code class="language-plaintext highlighter-rouge">300</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Bottom</td>
      <td><code class="language-plaintext highlighter-rouge">50</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="webcam-filter---sharpen">Webcam Filter - Sharpen</h5>

<p>I don’t use this filter any more.</p>

<h5 id="webcam-filter---chroma-key">Webcam Filter - Chroma Key</h5>

<p>Chromakeying is used to mask out the background of your camera’s field of view.  Think of the weather forecaster on TV standing in front of the weather map.  That image is superimposed by a computer by replacing the Chromakeying color with the computer image.  I have switched to a blue screen chromakey for my setup since my chair is blue.</p>

<p>You will probably mess around with these settings a lot until you get it just right.  The lighting in your room/office/studio is a big contributor to this.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Key Color Type</td>
      <td><code class="language-plaintext highlighter-rouge">Green</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Similarity</td>
      <td><code class="language-plaintext highlighter-rouge">440</code></td>
      <td><strong>Note</strong>: I play around with this number a lot depending on the lighting in my room.  I usually don’t go under <code class="language-plaintext highlighter-rouge">400</code> or higher than <code class="language-plaintext highlighter-rouge">450</code></td>
    </tr>
    <tr>
      <td>Smoothness</td>
      <td><code class="language-plaintext highlighter-rouge">80</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Key Color Spill Reduction</td>
      <td><code class="language-plaintext highlighter-rouge">100</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Opacity</td>
      <td><code class="language-plaintext highlighter-rouge">100</code></td>
      <td>Your personal preference. How much of yourself do you want on the screen?</td>
    </tr>
    <tr>
      <td>Contrast</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Brightness</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Gamma</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p><strong>Semi-pro</strong> tip: Don’t wear the same color shirt as your Chromakey :smile:</p>

<h4 id="microphone-filters">Microphone Filters</h4>

<p>These filters were based on a video I watched <a href="https://www.youtube.com/watch?v=JvFw1NsgElQ">Best Microphone Settings for Streamlabs OBS (2020)</a>. I recommend you watch this video to learn more about the audio filters.</p>

<h5 id="microphone-filter---gain">Microphone Filter - Gain</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Gain</td>
      <td><code class="language-plaintext highlighter-rouge">3.7</code></td>
      <td>The more gain, the louder the audio is</td>
    </tr>
  </tbody>
</table>

<h5 id="microphone-filter---noise-suppression">Microphone Filter - Noise Suppression</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>MEthod</td>
      <td><code class="language-plaintext highlighter-rouge">RNNoise</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="microphone-filter---noise-gate">Microphone Filter - Noise Gate</h5>

<p>These settings will require a lot of tweaking to get a clear sound for your recording. I find myself tweaking the Close and Open Thresholds mostly.  The video above does a great job of explaining each of the settings.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Close Threshold</td>
      <td><code class="language-plaintext highlighter-rouge">-53</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Open Threshold</td>
      <td><code class="language-plaintext highlighter-rouge">-26</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Attack Time</td>
      <td><code class="language-plaintext highlighter-rouge">25</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Hold Time</td>
      <td><code class="language-plaintext highlighter-rouge">200</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Release Time</td>
      <td><code class="language-plaintext highlighter-rouge">150</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="microphone-filter---compressor">Microphone Filter - Compressor</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Ratio</td>
      <td><code class="language-plaintext highlighter-rouge">10</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Threshold</td>
      <td><code class="language-plaintext highlighter-rouge">-18</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Attack</td>
      <td><code class="language-plaintext highlighter-rouge">6</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Release</td>
      <td><code class="language-plaintext highlighter-rouge">60</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Output Gain</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Sidechain/Ducking Source</td>
      <td><code class="language-plaintext highlighter-rouge">None</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h2 id="wrap-up">Wrap Up</h2>

<p>That’s it!  Again, your mileage may vary.  These are the ‘optimal’ settings for my hardware, software, and environment.</p>

<p>For details on my hardware, check out this <a href="/2020/12/20/coding-with-joeg-streaming-hardware-configuration-december-2020-update">post</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Twitch" /><category term="Stream" /><category term="CodingWithJoeG" /><category term="Archive" /><summary type="html"><![CDATA[In a previous post, I covered the hardware I use for the stream. In this post, I’ll cover the software and settings to ‘produce’ the stream and artifacts. Please keep in mind, this is a guide based on some of my searchings, my playing around, and my hardware, your needs may be different. Another thing to keep in mind, is these are my settings for streaming live coding not live gaming.]]></summary></entry><entry><title type="html">Coding with JoeG - Streaming Hardware Configuration - December 2020 Update</title><link href="https://www.josephguadagno.net/2020/12/20/coding-with-joeg-streaming-hardware-configuration-december-2020-update" rel="alternate" type="text/html" title="Coding with JoeG - Streaming Hardware Configuration - December 2020 Update" /><published>2020-12-20T04:05:00-07:00</published><updated>2020-12-20T04:05:00-07:00</updated><id>https://www.josephguadagno.net/2020/12/20/coding-with-joeg-streaming-hardware-configuration-december-2020-update</id><content type="html" xml:base="https://www.josephguadagno.net/2020/12/20/coding-with-joeg-streaming-hardware-configuration-december-2020-update"><![CDATA[<p>Since the original ‘<a href="/2020/05/20/coding-with-joeg-streaming-hardware-configuration">Stream Hardware Configuration</a> post, I’ve changed my hardware configuration a bit.  This post contains the most current hardware configuration.</p>

<p>I’ve listed out all of the equipment that I use to stream ‘Coding with JoeG’ on <a href="https://twitch.tv/jguadagno" target="_blank">Twitch</a>. While all this equipment is not necessary, it has helped me produce a reliable stream.  <em>Your mileage may vary!</em></p>

<h2 id="computer-equipment">Computer Equipment</h2>

<p>I’ve learned a little bit and upgraded my hardware setup.  Now I use a more powerful laptop with more memory and a better video card.  I now have a <a href="https://www.dell.com/en-us/work/shop/cty/pdp/spd/precision-15-5540-laptop/xctop554015us?gacd=9646510-1069-5763017-266828445-0&amp;dgc=st&amp;gclid=bd6cc4f26da8178727499b2d04620654&amp;gclsrc=3p.ds&amp;ds_rl=1282789&amp;msclkid=bd6cc4f26da8178727499b2d04620654" target="_blank">Dell Precision 5540</a> laptop.  The extra memory is key when you are using one pc. At minimum when I stream, I am running JetBrains Rider, StreamLabs OBS, and Microsoft Edge. Having the extra members keeps the operating system from swapping applications to the hard drive.</p>

<p>The next big change is the video card. This laptop has a higher-end (for laptops) video card, the NVIDIA Quadro T2000.</p>

<h3 id="streaming-pc">Streaming PC</h3>

<p>A <a href="https://www.dell.com/en-us/work/shop/cty/pdp/spd/precision-15-5540-laptop/xctop554015us?gacd=9646510-1069-5763017-266828445-0&amp;dgc=st&amp;gclid=bd6cc4f26da8178727499b2d04620654&amp;gclsrc=3p.ds&amp;ds_rl=1282789&amp;msclkid=bd6cc4f26da8178727499b2d04620654" target="_blank">Dell Precision 5540</a> with the specs of:</p>

<ul>
  <li>Intel i9-9880H (2.30Ghz) - 8 Cores</li>
  <li>32 Gb of memory</li>
  <li>512 Gb SSD (NVMe)</li>
  <li>NVIDIA Quadro T2000</li>
</ul>

<h3 id="secondary-pc">Secondary PC</h3>

<p class="notice--info">I no longer use a secondary computer thanks to the power of the primary streaming PC.</p>

<h2 id="streaming-gear">Streaming ‘Gear’</h2>

<h3 id="each-webcam-1080p">“Each” WebCam 1080p</h3>

<p>View on <a href="https://amzn.to/3e4kS3I" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-each-webcam.jpg" alt="Each Web Cam" class="align-center" /></p>

<p>While I could have used the built-in webcam on either laptop for the stream, the ‘Each’ webcam allows me to customize the webcam settings further. Luckily, with this camera, I don’t need to customize anything. The default, “out of the box”, settings produce an excellent quality 1080p image.  Another good thing about this camera is that I can mount on top of the laptop or tripod.</p>

<h3 id="audio-technica-atr2100x-usb-usbxlr-microphone-bundle-with-knox-gear-boom-arm-shock-mount-and-pop-filter">Audio-Technica ATR2100X-USB USB/XLR Microphone Bundle with Knox Gear Boom Arm, Shock Mount, and Pop Filter</h3>

<p>View on <a href="https://www.audio-technica.com/cms/wired_mics/b8dd84773f83092c/index.html" target="_blank">Manufacturers Site</a>
View on <a href="https://amzn.to/2XiLx5Q" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-atr2100x.jpg" alt="Audio-Technica ATR2100X-USB USB/XLR Microphone" class="align-center" /></p>

<p>A microphone is probably the one piece of hardware that I recommend you buy, not necessarily the ATR2100. I love the sound quality of this microphone so far.  Most built-in microphones are low quality and, on laptops, are next to the keyboard.  These two factors lead to the microphone picking up considerable extra noise.  Having a separate microphone mounted on a ‘Boom arm’ with the shock mount and pop filter reduces much noise.  With this configuration, you can’t hear me type, the fan in my office, or when I accidentally bang the desk.  This microphone is a great starter purchase.  As a bonus, this comes with an XLR connection to plug into a mixer board and other gear if you want to get serious about your audio.</p>

<h3 id="elgato-hd-60-s">Elgato HD 60 S+</h3>

<p class="notice--info"><strong><em>NOTE</em></strong> This is no longer used since I only use one PC.</p>

<p>View on <a href="https://www.elgato.com/en/gaming/game-capture-hd60-s-plus" target="_blank">Manufacturer Site</a>
View on <a href="https://amzn.to/2WOiVTi" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-hd60.jpg" alt="Elgato Game Capture HD60 S+" class="align-center" /></p>

<p>Don’t let the name ‘Game Capture’ fool you. This device does more than capture game footage.  I think the initial thought behind the product was for game capture. However, this device allows you to capture audio and video via HDMI and use it as a Video Source in OBS. <a href="https://howto.streamlabs.com/streamlabs-obs-19/elgato-capture-card-setup-in-streamlabs-obs-23" target="_blank">Setup</a> is fairly easy and everything you need is included.</p>

<h3 id="green-screening--chroma-keying">Green Screening / Chroma Keying</h3>

<p>“Green Screening” or chroma keying allows software like OBS to easily separate green screens and panels from the people standing in front of them and replace those backgrounds with pretty much anything.  You’ve seen chroma keying in Hollywood special effects when superheroes fly or while watching your local weather forecast when the weather person is magically standing in front of a map.  Many streamers use chroma keying to drop out everything behind them, so you only see them on the stream.  I use the ePhotoInc chroma key collapsible blue/green background with the Fovitec stand to hold it.</p>

<p>For OBS to remove or replace the background, you need to add a <code class="language-plaintext highlighter-rouge">Chromakey</code>.  You can check out the post ‘<a href="/2020/05/25/coding-with-joeg-streaming-software-configuration">Coding with JoeG - Software Configuration</a>’ for details on my configuration.</p>

<h4 id="ephotoinc-5-x-7-photo-collapsible-chromakey-green-blue-background-2-in-1-backdrop-panel-t57bg">ePhotoInc 5’ x 7’ Photo Collapsible CHROMAKEY Green Blue Background 2 in 1 Backdrop Panel T57BG</h4>

<p>View on <a href="https://amzn.to/2LJnoA3" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-chromakey.jpg" alt="ePhotoInc 5' x 7' Photo Collapsible CHROMAKEY Green Blue Background" class="align-center" /></p>

<h4 id="fovitec---76-clip-stand-kit-for-photo--video-pop-out-backdrops--reflectors">Fovitec - 7’6” Clip Stand Kit for Photo &amp; Video Pop-Out Backdrops &amp; Reflectors</h4>

<p>View on <a href="https://www.fovitec.com/products/76-clip-stand-kit-for-photo-video-pop-out-backdrops-reflectors" target="_blank">Manufacturers Site</a>
View on <a href="https://amzn.to/36fh0tX" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-stand-kit.jpg" alt="Fovitec - 7 foot 6 inch Clip Stand Kit for Photo &amp; Video Pop-Out Backdrops &amp; Reflectors" class="align-center" /></p>

<h2 id="helpful-extras">Helpful Extras</h2>

<h3 id="elgato-streamdeck">Elgato StreamDeck</h3>

<p>View on <a href="https://www.elgato.com/en/gaming/stream-deck" target="_blank">Manufacturers Site</a>
View on <a href="https://amzn.to/2XbYyON" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-streamdeck.jpg" alt="Elgato StreamDeck" class="align-center" /></p>

<p>The StreamDeck provides physical buttons that you can assign to an assortment of things.  You can program scene transitions, sound effects, send tweets, and more.  For me, I have it currently starting a few different applications and sending tweets.</p>

<p><del>I haven’t used the Elgato StreamDeck to the fullest extend yet.  I’ll play around with it some more this weekend.</del> I’ve since, starting using the Streamdeck a lot more, as you can see in the picture.  I’ll blog about the configuration another time.</p>

<p><img src="/assets/images/posts/streaming-equipment-elgato-streamdeck-config.png" alt="Elgato StreamDeck" class="align-center" /></p>

<h3 id="desk-lights">Desk Lights</h3>

<p>View on <a href="https://amzn.to/2TnLTHv" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-desk-light.jpg" alt="USB Powered Desk Lamp" class="align-center" /></p>

<p>I have 6 ‘Daylight’ bulbs in my home office, so the lighting is pretty good.  I use these lights mostly to highlight imperfections with shadows (mainly on my face).</p>

<h3 id="amazon-usb-30-hub">Amazon USB 3.0 Hub</h3>

<p class="notice--info"><strong>NOTE</strong> This is no longer used since I only use one PC.</p>

<p>View on <a href="https://amzn.to/3cNRw9B" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-amazon-usb-hub.jpg" alt="Amazon USB 3.0 Hub" class="align-center" /></p>

<p>A USB hub is not required.  However, because of my current configuration of laptops, I do not have enough USB ports for the microphone, webcam, Elgato card, and Stream Deck.</p>

<p class="notice--info"><strong>Note</strong>: This item is no longer available.</p>

<h3 id="wired-ethernet-connection">Wired Ethernet Connection</h3>

<p>While not required, it is strongly recommended depending on your network configuration.  The laptop hard-wired into my router (it’s super close to my desk).  Having the laptop hard-wired prevents wi-fi interference and potential packet/frame dropping.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>Your needs may vary.  This equipment list is what I purchased to deliver an inexpensive quality stream.  Depending on your hardware, you might not need anything from this list. I curated a list of the equipment for the Coding with JoeG Stream at this <a href="https://amzn.to/2ToLEvF">list</a> on Amazon.  <a href="https://www.twitch.tv/csharpfritz" target="_blank">Jeffrey Fritz</a>, part of the <a href="https://www.twitch.tv/team/livecoders" target="_blank">LiveCoders</a> team, also blogged about his configuration, in the <a href="https://jeffreyfritz.com/2019/01/live-streaming-setup-2019-edition/" target="_blank">Live Streaming Setup - 2019 Edition</a></p>

<p class="notice"><strong>Note</strong> If you click on an Amazon link and purchase a product, I may get a commission from Amazon. The purpose of the links is to avoid much searching and not to make money on the blog post.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Twitch" /><category term="Stream" /><category term="CodingWithJoeG" /><category term="Archive" /><summary type="html"><![CDATA[Since the original ‘Stream Hardware Configuration post, I’ve changed my hardware configuration a bit. This post contains the most current hardware configuration.]]></summary></entry><entry><title type="html">Thanksgiving and Silver Linings</title><link href="https://www.josephguadagno.net/2020/11/24/thanksgiving-and-silver-linings" rel="alternate" type="text/html" title="Thanksgiving and Silver Linings" /><published>2020-11-24T04:05:00-07:00</published><updated>2020-11-25T15:32:00-07:00</updated><id>https://www.josephguadagno.net/2020/11/24/thanksgiving-and-silver-linings</id><content type="html" xml:base="https://www.josephguadagno.net/2020/11/24/thanksgiving-and-silver-linings"><![CDATA[<p>While I am grateful for the old man bladder that woke me up at 2 am (AZ time) only to realize that my daughter was not home yet which kept me up (thank you parenting gene) which gave me all the energy in the world to wake up for the day, it did provide me with the energy to write this blog post. <strong><em>The Silver Lining</em></strong>. This time of the year in the U.S. we celebrate Thanksgiving, a holiday where we reflect and give thanks for the things we have. While 2020, and the pandemic has thrown a curve ball for many of us…</p>

<ul>
  <li>We’ve had people catch the virus or had a loved one who did</li>
  <li>We’ve had people that have had children during this</li>
  <li>We’ve had parents who have had to figure out how to be a full-time team member while being a full-time caregiver while being a full-time schoolteacher</li>
  <li>We’ve had to cancel travel plans and celebrations</li>
  <li>And so much more</li>
</ul>

<p>This Thanksgiving is going to be different for many of us. We will not be spending time with our loved ones to keep them and us safe. We won’t be lining up at Target at 5am to get that cool item that is 90% off retail. No, we won’t. Instead, we will be home keeping ourselves and loved ones safe. But if anything, the events of this year and last year have taught me to look at the <strong><em>Silver Lining</em></strong>. This Thanksgiving, there will be less frustration for me as the wife and mother-in-law battle it out for kitchen supremacy. For some, it might be skipping that argument with that crazy we all have or the fight over your favorite sportsball team. The point is, we all have something to be thankful for. Find your thing to be thankful for and focus on <strong>that</strong> <em>instead</em> of all the things you can’t do this year.</p>

<p>I am thankful that I and my immediate family are healthy and happy (for the most part). Find your happiness.</p>

<p>BTW, if you are home this Thanksgiving, and unable to “see” your relatives, Zoom is extending their typical 40-minute call limit this Thursday to enable families to be connected. If you just need someone to talk to, give me a call anytime or day. I’m around!</p>

<p>Have fun and be safe!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Personal" /><summary type="html"><![CDATA[While I am grateful for the old man bladder that woke me up at 2 am (AZ time) only to realize that my daughter was not home yet which kept me up (thank you parenting gene) which gave me all the energy in the world to wake up for the day, it did provide me with the energy to write this blog post. The Silver Lining. This time of the year in the U.S. we celebrate Thanksgiving, a holiday where we reflect and give thanks for the things we have. While 2020, and the pandemic has thrown a curve ball for many of us…]]></summary></entry><entry><title type="html">Adding .NET 5 Support to Azure App Service</title><link href="https://www.josephguadagno.net/2020/11/17/adding-dotnet5-support-to-azure-app-service" rel="alternate" type="text/html" title="Adding .NET 5 Support to Azure App Service" /><published>2020-11-17T10:05:00-07:00</published><updated>2020-11-21T16:23:00-07:00</updated><id>https://www.josephguadagno.net/2020/11/17/adding-dotnet5-support-to-azure-app-service</id><content type="html" xml:base="https://www.josephguadagno.net/2020/11/17/adding-dotnet5-support-to-azure-app-service"><![CDATA[<p>Yesterday I updated the <a href="https://www.github.com/jguadagno/contacts" target="_blank">Contacts</a> to .NET 5.  Now the process was pretty easy, you can watch the video <a href="https://youtu.be/9eD0WfVizbE" target="blank">here</a>, however once published to <a href="https://azure.microsoft.com/en-us/services/app-service/?wt.mc_id=DT-MVP-4024623" target="_blank">Azure App Service</a> I got the following error.</p>

<p><img src="/assets/images/posts/dotnet5-appservice-500-31.png" alt="HTTP Error 500.31" class="align-center" /></p>

<blockquote>
  <p>HTTP Error 500.31 - ANCM Failed to Find Native Dependencies<br />
Common solutions to this issue:<br />
The specified version of Microsoft.NetCore.App or Microsoft.AspNetCore.App was not found.</p>
</blockquote>

<p>This reminded of something that Scott Hunter mentioned at .NET Conf.  Azure App Service supports .NET 5, just not by default.</p>

<p>That reminded me that I had to check the configuration of the App Service to change it to enable support for .NET 5.  It’s was pretty easy to do.</p>

<ul>
  <li>Log in to your portal</li>
  <li>Navigate to your App Service</li>
  <li>Click on <em>Configuration</em></li>
</ul>

<p><img src="/assets/images/posts/dotnet5-appservice-configuration.png" alt="Configuration Setting" class="align-center" /></p>

<p>Under <em>Stack Setting</em>, change the following</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
    </tr>
    <tr>
      <td>Stack</td>
      <td><code class="language-plaintext highlighter-rouge">.NET</code></td>
    </tr>
    <tr>
      <td>.NET Framework Version</td>
      <td><code class="language-plaintext highlighter-rouge">.NET 5 (Early Access)</code></td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/dotnet5-appservice-stack-setting.png" alt=".NET 5 App Service Stack Settings" class="align-center" /></p>

<ul>
  <li>Click <em>Save</em></li>
  <li>Then go back to the <em>Overview</em> and restart the App Service.</li>
</ul>

<p>After refreshing your browser, the error should go away and the application load.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-in-net-5/#deploy-net-5-web-apps-to-azure-app-service-today?wt.mc_id=DT-MVP-4024623">Deploy .NET 5 Web Apps to Azure App Service Today</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term="Azure" /><category term="App Service" /><category term=".NET 5" /><summary type="html"><![CDATA[Yesterday I updated the Contacts to .NET 5. Now the process was pretty easy, you can watch the video here, however once published to Azure App Service I got the following error.]]></summary></entry><entry><title type="html">Embedding OneDrive Hosted PowerPoint slides in Jekyll Posts</title><link href="https://www.josephguadagno.net/2020/11/04/embedding-one-drive-hosted-powerpoint-slides-in-jekyll-posts" rel="alternate" type="text/html" title="Embedding OneDrive Hosted PowerPoint slides in Jekyll Posts" /><published>2020-11-04T18:50:00-07:00</published><updated>2020-11-28T09:05:00-07:00</updated><id>https://www.josephguadagno.net/2020/11/04/embedding-one-drive-hosted-powerpoint-slides-in-jekyll-posts</id><content type="html" xml:base="https://www.josephguadagno.net/2020/11/04/embedding-one-drive-hosted-powerpoint-slides-in-jekyll-posts"><![CDATA[<p>I was chatting with Dave Brock (<a href="https://twitter.com/daveabrock" target="_blank">@daveabrock</a>) about our .NET 5 presentations today (<a href="/presentations/dotnet5-what-is-it">.NET 5 - What is it?</a>). He mentioned that he wanted to “steal” my PowerPoint embed feature.  Dave’s a nice guy and all but is it really stealing if you ask for it? :smile:</p>

<p>Here is what the page looks like on my site.</p>

<p><img src="/assets/images/posts/embed-powerpoint-example.png" alt="Screen shot" class="align-center" /></p>

<p>I host all of my presentation decks on OneDrive and make them available to all the attendees of my talks.  This allows me to embed the links in any emails or sites that I want to. I embed all the talks, code links, and sample videos on each of the talks’ respective pages on my <a href="/presentations/">site</a>. With my site is built using <a href="https://jekyllrb.com/" target="_blank">Jekyll</a> and hosted on GitHub pages, embedding the slide decks was quite simple. Now here is the how?</p>

<h2 id="get-the-embed-code-from-onedrive">Get the Embed Code from OneDrive</h2>

<p>First, navigate to the file you want to embed in your online (this needs to be done via the web client) OneDrive file lists and select it.</p>

<p><img src="/assets/images/posts/embed-powerpoint-select-file.png" alt="Screen shot" class="align-center" /></p>

<p>Next, you will see on the toolbar, an <code class="language-plaintext highlighter-rouge">&lt;/&gt;Embed</code> button. I encased it in red. Click on the <code class="language-plaintext highlighter-rouge">&lt;/&gt;Embed</code> button and you will see something like this.</p>

<p><img src="/assets/images/posts/embed-powerpoint-embed.png" alt="Screen shot" class="align-center" /></p>

<p>Copy out the HTML, although for this to work, you only need the <code class="language-plaintext highlighter-rouge">src</code> value.  In this example, it is <code class="language-plaintext highlighter-rouge">https://onedrive.live.com/embed?cid=406EE4C95978C038&amp;resid=406EE4C95978C038%2179191&amp;authkey=AFFYuImKsNsScF4&amp;em=2</code></p>

<p>That’s it from OneDrive.</p>

<h2 id="updating-jekyll">Updating Jekyll</h2>

<p>Initially, this is going to be a two-part process. It’s two-part because I have my <em>presentations</em> pages as a separate <a href="https://jekyllrb.com/docs/layouts/" target="_blank">layout</a> in Jekyll. It is built as a presentation layout.</p>

<h3 id="setup-up-the-layout">Setup up the Layout</h3>

<p>First I created a <a href="https://jekyllrb.com/docs/collections/" target="_blank">collection</a> for the presentation layout.  Here is the section of my <code class="language-plaintext highlighter-rouge">_config.yml</code>.</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="na">collections</span><span class="pi">:</span>
  <span class="na">presentations</span><span class="pi">:</span>
    <span class="na">output</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">permalink</span><span class="pi">:</span> <span class="s">/presentations/:name</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then, further down in the <code class="language-plaintext highlighter-rouge">_config.yml</code> I added a <code class="language-plaintext highlighter-rouge">default</code> section to handle the pages for the presentations. This is so I don’t have to add the <code class="language-plaintext highlighter-rouge">layout</code> front matter to every presentation.</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="c1"># Defaults</span>
<span class="na">defaults</span><span class="pi">:</span>
  <span class="c1"># _presentations</span>
  <span class="pi">-</span> <span class="na">scope</span><span class="pi">:</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
      <span class="na">type</span><span class="pi">:</span> <span class="s">presentations</span>
    <span class="na">values</span><span class="pi">:</span>
      <span class="na">layout</span><span class="pi">:</span> <span class="s">presentation</span>
      <span class="na">share</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">classes </span><span class="pi">:</span> <span class="s">wide</span>
      <span class="na">author_profile</span><span class="pi">:</span> <span class="no">true</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><a href="https://github.com/jguadagno/jguadagno.github.io/blob/master/_config.yml" target="_blank">Full _config.yml</a></p>

<p>Next up is creating the layout.  Create a folder in the root of your Jekyll site, I called mine <code class="language-plaintext highlighter-rouge">_layout</code>.  The underscore is important for Jekyll as it won’t ‘publish’ folders with an underscore. Then in that folder create a file and name it <code class="language-plaintext highlighter-rouge">presentation.html</code>. <strong><em>Note</em></strong>: This name should match the name in the <code class="language-plaintext highlighter-rouge">values.layout</code> that is defined in the defaults, without the <code class="language-plaintext highlighter-rouge">.html</code></p>

<p>I hide some of the other parts of the HTML so that we can focus on the PowerPoint embedding.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre>{{ content }}

{% if page.powerPointUrl %}
<span class="nt">&lt;h3&gt;</span>Slide Deck<span class="nt">&lt;/h3&gt;</span>
<span class="nt">&lt;iframe</span> <span class="na">src=</span><span class="s">"{{page.powerPointUrl}}"</span> <span class="na">width=</span><span class="s">"610px"</span> <span class="na">height=</span><span class="s">"367px"</span> <span class="na">frameborder=</span><span class="s">"0"</span><span class="nt">&gt;&lt;/iframe&gt;</span>
{% endif %}
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Line 1 is where the content of the presentation is displayed. More on that later.</p>

<p>Line 3 checks to see if there is a page attribute of <code class="language-plaintext highlighter-rouge">powerPointUrl</code> that is present. To do this, we’ll create some <a href="https://jekyllrb.com/docs/front-matter/" target="_blank">Front Matter</a> for our presentation. Again, more on that later.</p>

<p>Line 4 and 5 is where I <em>embed</em> the PowerPoint. I recreate the HTML that OneDrive provided.</p>

<p>Line 6 closes the conditional statement of line 1.</p>

<p class="notice--info"><strong><em>Bonus</em></strong> If you look at the <a href="https://github.com/jguadagno/jguadagno.github.io/blob/master/_layouts/presentation.html" target="_blank">full source</a> code, you can embed YouTube videos also.</p>

<h3 id="presentation-pages">Presentation Pages</h3>

<p>Now that we have created the layout for the Presentation page, in this example, let’s look at a sample presentation.</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="nn">---</span>
<span class="na">title</span><span class="pi">:</span> <span class="s">.NET 5 - What is it?</span>
<span class="na">isKeynote</span><span class="pi">:</span> <span class="no">false</span>
<span class="na">isRetired</span><span class="pi">:</span> <span class="no">false</span>
<span class="na">sourceUrl</span><span class="pi">:</span>
<span class="na">powerPointUrl</span><span class="pi">:</span> <span class="s">https://onedrive.live.com/embed?cid=406EE4C95978C038&amp;resid=406EE4C95978C038%2179191&amp;authkey=AFFYuImKsNsScF4&amp;em=2</span>
<span class="nn">---</span>
<span class="s">We have the .NET Framework, .NET Standard, .NET Core, ASP.NET, ASP.NET Core ... do not get me started on Classic ASP or other platforms :). Where are we going with .NET? What is .NET 5? What is going to happen to these 'legacy' frameworks? Let us take a look at the past, the present, and the future of .NET. After this talk, you will have a good understanding of where Microsoft is taking the platform and where you can focus your development efforts.</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Line 6 we provide the Url for the PowerPoint presentation.</p>

<p>You can see a more ‘complex’ sample by checking out <a href="https://github.com/jguadagno/jguadagno.github.io/blob/master/_presentations/typescript-for-the-microsoft-developer.md" target="_blank">Typescript for the Microsoft Developer</a></p>

<h2 id="want-more">Want More</h2>

<p>You can check out all of the ‘source code’ for the site on the GitHub <a href="https://github.com/jguadagno/jguadagno.github.io" target="_blank">repo</a>.</p>

<p>If you want to see how I ‘dynamically’ generate the <a href="/presentations/">presentations</a> page, checkout <a href="https://github.com/jguadagno/jguadagno.github.io/blob/master/_pages/presentations.md">_pages/presentations.md</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Jekyll" /><category term="OneDrive" /><category term="PowerPoint" /><summary type="html"><![CDATA[I was chatting with Dave Brock (@daveabrock) about our .NET 5 presentations today (.NET 5 - What is it?). He mentioned that he wanted to “steal” my PowerPoint embed feature. Dave’s a nice guy and all but is it really stealing if you ask for it? :smile:]]></summary></entry><entry><title type="html">NLog, Dependency Injection, and Azure Functions, Oh My!</title><link href="https://www.josephguadagno.net/2020/11/03/nlog-dependency-injection-and-azure-functions-oh-my" rel="alternate" type="text/html" title="NLog, Dependency Injection, and Azure Functions, Oh My!" /><published>2020-11-03T12:00:00-07:00</published><updated>2020-11-05T16:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/11/03/nlog-dependency-injection-and-azure-functions-oh-my</id><content type="html" xml:base="https://www.josephguadagno.net/2020/11/03/nlog-dependency-injection-and-azure-functions-oh-my"><![CDATA[<p class="notice--info"><strong><em>Update November 5th, 2020</em></strong> Thanks to Rolf Kristensen, one of the NLog maintainers, and some debugging, I came up with another solution!</p>

<p>I’ve been working on a side project <a href="https://github.com/jguadagno/jjgnet-broadcast" target="_blank">JosephGuadagno.Net Broadcasting</a>; I know I need a better name :smile:, for a month or so now. The project’s goal is to provide a way for me to promote talks, scheduled <a href="https://jjg.me/stream">streams</a>, my YouTube <a href="https://jjg.me/youtube">Videos</a>, and blog content on social media.  This project is a collection of Azure Functions that perform different tasks like query the YouTube Apis, check RSS feeds, post to Facebook feeds, etc.  The project, more so the components that make it up, has started to get quite large. Add the fact that the solution is running in Azure on someone else computer, I wanted to add some logging and telemetry to the components to know what was happening and when. I added <a href="https://nlog-project.org/">NLog</a> to the project to help with the logging. <a href="https://azure.microsoft.com/en-us/services/monitor/?wt.mc_id=DT-MVP-4024623#product-overview">Azure Monitor</a>, aka <a href="https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview?wt.mc_id=DT-MVP-4024623" target="_blank">Application Insights</a>, is coming next :smile:. If you need to add logging to your application, I suggest you take a look at NLog, it’s pretty easy to use once you get the configuration right.  And here lies the reason for blog posts…</p>

<p>If you haven’t used NLog before, like most logging frameworks, it needs a configuration to run, this configuration is typically in a <code class="language-plaintext highlighter-rouge">nlog.config</code> <a href="https://github.com/NLog/NLog/wiki/Tutorial#configure-nlog-targets-for-output" target="_blank">file</a>, although with some updates to the project, you can use an <code class="language-plaintext highlighter-rouge">appsettings.json</code> <a href="https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-configuration-with-appsettings.json" target="_blank">file</a>.  Honestly, I think the project is trying to catch up to the configuration system in ASP.NET Core. The framework looks for the <code class="language-plaintext highlighter-rouge">nlog.config</code> file in the same folder as the assembly is being executed from.  For Azure Functions, that folder varies depending on where you are running it.  If like me, you are running it through JetBrains Rider, it runs it from a really long directory (the installation directory of the Azure Functions framework/tools). This location is not ideal, in my opinion, for the <code class="language-plaintext highlighter-rouge">nlog.config</code>.  I would prefer it in the application directory.  So far there really isn’t a problem, NLog provides the ability to change the location of the configuration file.  I tried that using the following code in my <code class="language-plaintext highlighter-rouge">Startup.cs</code></p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="nf">Startup</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">LogManager</span><span class="p">.</span><span class="nf">Setup</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">SetupExtensions</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="nf">AutoLoadAssemblies</span><span class="p">(</span><span class="k">false</span><span class="p">))</span>
        <span class="p">.</span><span class="nf">LoadConfigurationFromFile</span><span class="p">(</span><span class="n">Environment</span><span class="p">.</span><span class="n">CurrentDirectory</span> <span class="p">+</span> <span class="n">Path</span><span class="p">.</span><span class="n">DirectorySeparatorChar</span> <span class="p">+</span> <span class="s">"nlog.config"</span><span class="p">,</span> <span class="n">optional</span><span class="p">:</span> <span class="k">false</span><span class="p">)</span>
        <span class="p">.</span><span class="nf">LoadConfiguration</span><span class="p">(</span><span class="n">builder</span> <span class="p">=&gt;</span> <span class="n">builder</span><span class="p">.</span><span class="n">LogFactory</span><span class="p">.</span><span class="n">AutoShutdown</span> <span class="p">=</span> <span class="k">false</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Oh, did I mention, I was using the <em>new</em> Azure Functions <a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection?wt.mc_id=DT-MVP-4024623" target="_blank">Dependency Injection</a>. On line 5, I tell NLog to load the configuration from <code class="language-plaintext highlighter-rouge">Environment.CurrentDirectory + Path.DirectorySeparatorChar + "nlog.config"</code> which on my local machine translates to something like <code class="language-plaintext highlighter-rouge">c:\MyProjects\FunctionApp\nlog.config</code>.  Running a few tests locally, everything was working and I was getting logs. Once I committed the code, the code was published to the Azure Function via the <a href="https://github.com/jguadagno/jjgnet-broadcast/blob/main/.github/workflows/publish-to-azure-function.yml" target="_blank">GitHub Action</a>, I noticed I wasn’t getting any logs.  In fact, I was getting an error message:</p>

<blockquote>
  <p>System.Private.CoreLib: Exception has been thrown by the target of an invocation. NLog: Failed to load NLog LoggingConfiguration. ‘D:\Program Files...’</p>
</blockquote>

<p>I’ve left out the full file path that was provided.</p>

<p>This led me to use the Azure Functions Advanced Tools (<a href="https://github.com/projectkudu/kudu" target="_blank">Project Kudo</a>) to start some directory browsing to make sure I copied the <code class="language-plaintext highlighter-rouge">nlog.config</code> file to the root of the application.</p>

<p><img src="/assets/images/posts/nlog-di-az-functions-folder-lists.png" alt="Folder List" class="align-center" /></p>

<p>The file is there! What could be the problem? The first clue was the exception thrown about searching in the path ‘D:\Program Files…'. Based on the code sample above, line 5, <code class="language-plaintext highlighter-rouge">Environment.CurrentDirectory + Path.DirectorySeparatorChar + "nlog.config"</code> I should be pulling the configuration from <code class="language-plaintext highlighter-rouge">D:\home\site\wwwroot</code> but I wasn’t.  Now, I could have hard-coded the value on line 5 and stopped there but I didn’t.  After some research, it looks like I could get the <code class="language-plaintext highlighter-rouge">ExecutionContext</code> to get the application directory that the function is running in. But as the <a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection?wt.mc_id=DT-MVP-4024623#caveats" target="_blank">docs</a> say…</p>

<blockquote>
  <p>The dependency injection container only holds explicitly registered types. The only services available as injectable types are what are setup in the Configure method. As a result, Functions-specific types like BindingContext and ExecutionContext aren’t available during setup or as injectable types.</p>
</blockquote>

<p>Well, that stinks! Back to the drawing board! Now I had to figure out how do I get the current folder based on <em>where the code is running</em>, reflection isn’t easy to get right, the folder structure varies depending on the tools you are using, Rider, Visual Studio, Visual Studio Code, etc. I needed to map the code locally to something like <code class="language-plaintext highlighter-rouge">C:\MyProjects\FunctionApp</code> on Windows, <code class="language-plaintext highlighter-rouge">~/Projects/FunctionApp</code> on Mac, and <code class="language-plaintext highlighter-rouge">d:/Home/site/wwwroot/</code> for Azure.  There was no Environment variable to do this so this is what I came up with</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">localRoot</span> <span class="p">=</span> <span class="n">Environment</span><span class="p">.</span><span class="nf">GetEnvironmentVariable</span><span class="p">(</span><span class="s">"AzureWebJobsScriptRoot"</span><span class="p">);</span>
<span class="kt">var</span> <span class="n">azureRoot</span> <span class="p">=</span> <span class="s">$"</span><span class="p">{</span><span class="n">Environment</span><span class="p">.</span><span class="nf">GetEnvironmentVariable</span><span class="p">(</span><span class="s">"HOME"</span><span class="p">)}</span><span class="s">/site/wwwroot"</span><span class="p">;</span>

<span class="kt">var</span> <span class="n">_applicationDirectory</span> <span class="p">=</span> <span class="n">localRoot</span> <span class="p">??</span> <span class="n">azureRoot</span><span class="p">;</span>
<span class="n">LogManager</span><span class="p">.</span><span class="nf">Setup</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">SetupExtensions</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="nf">AutoLoadAssemblies</span><span class="p">(</span><span class="k">false</span><span class="p">))</span>
    <span class="p">.</span><span class="nf">LoadConfigurationFromFile</span><span class="p">(</span><span class="n">_applicationDirectory</span> <span class="p">+</span> <span class="n">Path</span><span class="p">.</span><span class="n">DirectorySeparatorChar</span> <span class="p">+</span> <span class="s">"nlog.config"</span><span class="p">,</span> <span class="n">optional</span><span class="p">:</span> <span class="k">false</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">LoadConfiguration</span><span class="p">(</span><span class="n">configurationBuilder</span> <span class="p">=&gt;</span> <span class="n">configurationBuilder</span><span class="p">.</span><span class="n">LogFactory</span><span class="p">.</span><span class="n">AutoShutdown</span> <span class="p">=</span> <span class="k">false</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Line 1 determines the path if running locally. The environment variable <code class="language-plaintext highlighter-rouge">AzureWebJobsScriptRoot</code> is <code class="language-plaintext highlighter-rouge">null</code> when running on Azure.</p>

<p>Line 2 creates the path when running in Azure.  The environment variable <code class="language-plaintext highlighter-rouge">HOME</code> points to the folder the App Server that is running your Function(s) is running out of.</p>

<p>Line 4 creates the <code class="language-plaintext highlighter-rouge">_applicationDirectory</code> variable based on whether or not <code class="language-plaintext highlighter-rouge">localroot</code> is null.</p>

<p>This solved the problem running locally and in Azure.  I hope in version 4 of the Azure Function SDK, the directory, environment variables, and how settings are handled is a little more consistent.</p>

<h2 id="alternate-solution">Alternate Solution</h2>

<p>It looks like the <code class="language-plaintext highlighter-rouge">ExecutionContext</code> works with a slight modification. I placed this code in the <code class="language-plaintext highlighter-rouge">Configure</code> method to come up with the application directory.  This works both locally and when in an Azure Function.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">executionContextOptions</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="nf">BuildServiceProvider</span><span class="p">()</span>
    <span class="p">.</span><span class="n">GetService</span><span class="p">&lt;</span><span class="n">IOptions</span><span class="p">&lt;</span><span class="n">ExecutionContextOptions</span><span class="p">&gt;&gt;().</span><span class="n">Value</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">currentDirectory</span> <span class="p">=</span> <span class="n">executionContextOptions</span><span class="p">.</span><span class="n">AppDirectory</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now, I can initialize NLog in the <code class="language-plaintext highlighter-rouge">Configure</code> method like I can other classes and DI.  <strong><em>Note</em></strong> I just initialize NLog, it is not recommended to log in the Constructor.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="n">LogManager</span><span class="p">.</span><span class="nf">Setup</span><span class="p">()</span>
  <span class="p">.</span><span class="nf">SetupExtensions</span><span class="p">(</span><span class="n">e</span> <span class="p">=&gt;</span> <span class="n">e</span><span class="p">.</span><span class="nf">AutoLoadAssemblies</span><span class="p">(</span><span class="k">false</span><span class="p">))</span>
  <span class="p">.</span><span class="nf">LoadConfigurationFromFile</span><span class="p">(</span><span class="n">currentDirectory</span> <span class="p">+</span> <span class="n">Path</span><span class="p">.</span><span class="n">DirectorySeparatorChar</span> <span class="p">+</span> <span class="s">"nlog.config"</span><span class="p">,</span> <span class="n">optional</span><span class="p">:</span> <span class="k">false</span><span class="p">)</span>
  <span class="p">.</span><span class="nf">LoadConfiguration</span><span class="p">(</span><span class="n">configurationBuilder</span> <span class="p">=&gt;</span> <span class="n">configurationBuilder</span><span class="p">.</span><span class="n">LogFactory</span><span class="p">.</span><span class="n">AutoShutdown</span> <span class="p">=</span> <span class="k">false</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="wrap-up">Wrap Up</h2>

<p>Well, I hope this helps you and save you a few hours getting NLog to work in Azure Function and potentially any file/folder work in Azure.</p>

<h2 id="bonus">Bonus</h2>

<p><em>You can declare the <code class="language-plaintext highlighter-rouge">_applicationDirectory</code> variable as a private variable in the Startup class of your function and then config the Configuration system to load environment-specific settings with the following code.</em></p>

<p>Now you can use the <code class="language-plaintext highlighter-rouge">currentDirectory</code> variable that is declared in the <code class="language-plaintext highlighter-rouge">Configure</code> method to get the directory/path to your configuration file</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">config</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ConfigurationBuilder</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">SetBasePath</span><span class="p">(</span><span class="n">currentDirectory</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">AddJsonFile</span><span class="p">(</span><span class="s">"local.settings.json"</span><span class="p">,</span> <span class="k">true</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">AddUserSecrets</span><span class="p">(</span><span class="n">Assembly</span><span class="p">.</span><span class="nf">GetExecutingAssembly</span><span class="p">(),</span> <span class="k">true</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">AddEnvironmentVariables</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>My <a href="https://github.com/jguadagno/jjgnet-broadcast/blob/main/src/JosephGuadagno.Broadcasting.Functions/Startup.cs" target="_blank">Startup.cs</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Azure Functions" /><category term="NLog" /><category term="Dependency Injection" /><summary type="html"><![CDATA[Update November 5th, 2020 Thanks to Rolf Kristensen, one of the NLog maintainers, and some debugging, I came up with another solution!]]></summary></entry><entry><title type="html">Working with Microsoft Identity - React Native Client</title><link href="https://www.josephguadagno.net/2020/10/24/working-with-microsoft-identity-react-native-client" rel="alternate" type="text/html" title="Working with Microsoft Identity - React Native Client" /><published>2020-10-24T00:00:00-07:00</published><updated>2020-10-24T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/10/24/working-with-microsoft-identity-react-native-client</id><content type="html" xml:base="https://www.josephguadagno.net/2020/10/24/working-with-microsoft-identity-react-native-client"><![CDATA[<p>In this post, I’m going to walk through how you authenticate and use an API that is secured with Azure Active Directory using React Native and the Microsoft Identity library.</p>

<blockquote>
  <p>This blog post demonstrates connecting to the Contact API that I have been building on my stream  <a href="https://jjg.me/stream" target="_blank">Coding with JoeG</a>. The API project can be found in the <a href="https://github.com/jguadagno/contacts" target="_blank">Contacts</a> Repository. While the code will build, it will not run because you will need the client application registered in Azure with the correct permissions.</p>
</blockquote>

<p>You can view the following <a href="https://youtu.be/IScDA7cKsSM" target="_blank">Introduction to React Native</a> video recording from my stream.  You can also watch me code this live on <a href="https://youtu.be/9Wot8p9bWf4" target="_blank">Building the API Client and Authentication</a>.</p>

<p>Completed code repository at <a href="https://github.com/jguadagno/contacts-react-native-client" target="_blank">https://github.com/jguadagno/contacts-react-native-client</a></p>

<h2 id="prerequisites">Prerequisites</h2>

<p>This blog post assumes that you have certain tools installed and are familiar with them. If you don’t have the tools installed, I have provided a quick guide and links to get you started.</p>

<h3 id="nodejs">Node.js</h3>

<p>Visit the Node.js <a href="https://nodejs.org/en/" target="_blank">installation</a> for details on installing Node.js.</p>

<p>After node.js is installed, you can optionally load the required packages that will be used later ahead of time so the installation goes faster.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>npm <span class="nb">install</span> <span class="nt">-g</span> expo-cli msal @openapitools/openapi-generator-cli @react-native-community/masked-view react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-navigation/native @react-navigation/stack axios url
</pre></td></tr></tbody></table></code></pre></div></div>

<p>or with Yarn</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>yarn global add expo-cli msal @openapitools/openapi-generator-cli @react-native-community/masked-view react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-navigation/native @react-navigation/stack axios url
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="react-native">React Native</h3>

<p>Installing React Native</p>

<ul>
  <li><a href="https://reactnative.dev/" target="_blank">React Native</a></li>
  <li><a href="https://reactnative.dev/docs/environment-setup" target="_blank">Setting up your environment</a></li>
</ul>

<p>Once installed, add the expo cli to your project.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>yarn add expo-cli
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="generate-react-native-application">Generate React Native Application</h3>

<p>To generate the React Native application, execute the following commands in your terminal or command prompt. Replace <code class="language-plaintext highlighter-rouge">my-app</code> with the name you want to call your application.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="c"># Create the React Native project using the TypeScript template</span>
npx create-react-native-app my-app <span class="nt">--template</span> with-typescript
<span class="c"># Install additional React Native tools: React Navigation</span>
yarn add @react-navigation/native @react-navigation/stack
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="setup-openapi-generator">Setup OpenAPI Generator</h3>

<p>The <a href="https://github.com/OpenAPITools/openapi-generator" target="_blank">OpenAPI Generator</a> is used to generate an API client for the React Native application to use.  You can pick from a few different <a href="https://openapi-generator.tech/docs/generators" target="_blank">generators</a> but for this example, I am using the <a href="https://github.com/axios/axios" target="_blank">Axios</a> template named ‘<a href="https://openapi-generator.tech/docs/generators/typescript-axios" target="_blank">typescript-axios</a>’.</p>

<p>Using Yarn, we can create a command that will generate our API client fairly easily. Open up a command prompt or terminal in the directory of the project and execute the following commands. <strong><em>Note</em></strong>: change <code class="language-plaintext highlighter-rouge">my-app</code> to the application name of your React Native project.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="c"># CD into the project</span>
<span class="nb">cd </span>my-app
<span class="c"># Add axios project dependencies</span>
yarn add axios url
<span class="c"># Add client generator (as Dev dependency)</span>
yarn add <span class="nt">-D</span> @openapitools/openapi-generator-cli
<span class="c"># Create api folder (for everything API related). It should be lowercase to avoid warnings</span>
<span class="nb">mkdir </span>api
<span class="c"># Download Open API file to api folder</span>
curl https://cwjg-contacts-api.azurewebsites.net/swagger/v1/swagger.json <span class="o">&gt;</span> ./api/openapi.json
<span class="c"># Add generator script to package.json</span>
npx add-project-script <span class="nt">-n</span> <span class="s2">"openapi"</span> <span class="nt">-v</span> <span class="s2">"openapi-generator-cli generate -i ./api/openapi.json -g typescript-axios -o ./api/generated"</span>
<span class="c"># Generate the client (requires JDK installed)</span>
yarn openapi
</pre></td></tr></tbody></table></code></pre></div></div>

<p>If your API changes and you need to update your API client, just execute the following command</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>yarn openapi
</pre></td></tr></tbody></table></code></pre></div></div>

<p>to generate a new client</p>

<h2 id="getting-started">Getting started</h2>

<h3 id="create-the-api-client">Create the API client</h3>

<blockquote>
  <p>For the sake of this blog post, I named my application <strong><em>Contacts</em></strong> with it using <strong><em>ContactsApi</em></strong> as the name of my API client. You can replace <code class="language-plaintext highlighter-rouge">Contacts</code> with <code class="language-plaintext highlighter-rouge">my-app</code> or whatever you called your application. This blog post also assumes we are working on a brand new React Native Application.</p>
</blockquote>

<p>The first step is to expose the API client to our React Native application.</p>

<p>In the <code class="language-plaintext highlighter-rouge">./Contacts/api</code> folder, create a new file <code class="language-plaintext highlighter-rouge">index.ts</code> with the following contents</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="k">import</span> <span class="p">{</span> <span class="nx">ContactsApi</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./generated</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
    <span class="na">Contacts</span><span class="p">:</span> <span class="k">new</span> <span class="nx">ContactsApi</span><span class="p">()</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now that the API is exposed, let’s use the API.  Navigate to the <code class="language-plaintext highlighter-rouge">App.tsx</code> file, in the project root folder, import the <code class="language-plaintext highlighter-rouge">Api</code>.  <em>Note</em>: if you are using an IDE, it could insert this <code class="language-plaintext highlighter-rouge">import</code> for you.</p>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">import</span> <span class="nx">Api</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./api</span><span class="dl">'</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now we want to consume the API. Replace the HTML in the  <code class="language-plaintext highlighter-rouge">return()</code> block with this.</p>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="p">&lt;</span><span class="nc">View</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">container</span><span class="si">}</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nc">Button</span> <span class="na">title</span><span class="p">=</span><span class="s">"Hello"</span> <span class="na">onPress</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">Api</span><span class="p">.</span><span class="nx">Contacts</span><span class="p">.</span><span class="nx">contactsGet</span><span class="p">();</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello</span><span class="dl">"</span><span class="p">);}</span><span class="si">}</span> <span class="p">/&gt;</span>
<span class="p">&lt;/</span><span class="nc">View</span><span class="p">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>On the third line, you will see that we are calling <code class="language-plaintext highlighter-rouge">Api.Contacts.contactsGet();</code>. We are not doing anything with the <code class="language-plaintext highlighter-rouge">list</code> variable yet. We are just getting the API <em>connected</em>.  Now the call to the API will not work yet because we are still <em>wiring it up</em>.  If you want to see it working, you can execute the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>yarn web
</pre></td></tr></tbody></table></code></pre></div></div>

<p>in your terminal to start up the application.  Clicking on the ‘Hello’ button will log the word ‘Hello’ to the developer tools console.</p>

<h3 id="configure-security">Configure security</h3>

<p>Now we have to wire up the security.  To do that we first need to install the NPM Package for Microsoft Authentication Library (MSAL). <strong>Note</strong>: Make sure you are in the <code class="language-plaintext highlighter-rouge">Contact</code> directory in your terminal session.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>yarn add msal
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Once the package is done installing, create a folder in <code class="language-plaintext highlighter-rouge">Contacts</code> called <code class="language-plaintext highlighter-rouge">msal</code>. This folder will be used for the classes that interact with the Microsoft Identity library. You will need to create three files in the <code class="language-plaintext highlighter-rouge">msal</code> folder.</p>

<h4 id="irequestconfigurationts">IRequestConfiguration.ts</h4>

<p>This file contains the class the represents the request with scopes.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">export</span> <span class="k">default</span> <span class="kr">interface</span> <span class="nx">IRequestConfiguration</span> <span class="p">{</span>
    <span class="nl">scopes</span><span class="p">:</span> <span class="kr">string</span><span class="p">[];</span>
    <span class="nl">state</span><span class="p">?:</span> <span class="kr">string</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><a href="https://gist.github.com/jguadagno/56fa09b2e5498824795e3a41b2629866/raw/f5797bade7486ecd2fc5322c8a558d6606b562bc/IRequestConfiguration.ts" target="_blank">Download</a></p>

<h4 id="msalconfigts">MsalConfig.ts</h4>

<p>This file contains the configuration for the library.  You will need to edit this class based on your Azure and authentication configuration. You’ll need to at minimum change the <code class="language-plaintext highlighter-rouge">config.auth.clientId</code> to match that of the Azure client id.</p>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="rouge-code"><pre><span class="kd">const</span> <span class="nx">MsalConfig</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">config</span><span class="p">:</span> <span class="p">{</span>
        <span class="c1">// Azure Credentials</span>
        <span class="na">auth</span><span class="p">:</span> <span class="p">{</span>
            <span class="na">clientId</span><span class="p">:</span> <span class="dl">''</span><span class="p">,</span> <span class="c1">// Replace with your client id</span>
            <span class="na">authority</span><span class="p">:</span> <span class="dl">"</span><span class="s2">https://login.microsoftonline.com/common</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">redirectUri</span><span class="p">:</span> <span class="dl">"</span><span class="s2">https://localhost:19006/Auth</span><span class="dl">"</span><span class="p">,</span>
            <span class="na">navigateToLoginRequestUrl</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
            <span class="na">validateAuthority</span><span class="p">:</span> <span class="kc">false</span>
        <span class="p">},</span>
        <span class="na">cache</span><span class="p">:</span> <span class="p">{</span>
            <span class="na">cacheLocation</span><span class="p">:</span> <span class="dl">"</span><span class="s2">sessionStorage</span><span class="dl">"</span> <span class="c1">// session storage is more secure, but prevents single-sign-on from working. other option is 'localStorage'</span>
        <span class="p">}</span> <span class="k">as</span> <span class="kd">const</span>
    <span class="p">},</span>
    <span class="c1">// The default scopes are listed here since the scopes for individual pages may be different</span>
    <span class="c1">// Replace there with any default scopes you need for your application.</span>
    <span class="na">defaultRequestConfiguration</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">scopes</span><span class="p">:</span> <span class="p">[</span><span class="dl">"</span><span class="s2">scope1</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">scope2</span><span class="dl">"</span><span class="p">]</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="k">export</span> <span class="k">default</span> <span class="nx">MsalConfig</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><a href="https://gist.github.com/jguadagno/56fa09b2e5498824795e3a41b2629866/raw/f5797bade7486ecd2fc5322c8a558d6606b562bc/MsalConfig.ts" target="_blank">Download</a></p>

<h4 id="msalhandlerts">MsalHandler.ts</h4>

<p>This file contains the interactions between your application and the Microsoft Identity library.</p>

<div class="language-typescript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
</pre></td><td class="rouge-code"><pre><span class="k">import</span> <span class="p">{</span> <span class="nx">UserAgentApplication</span><span class="p">,</span> <span class="nx">AuthResponse</span><span class="p">,</span> <span class="nx">AuthError</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">msal</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">MsalConfig</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./MsalConfig</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">IRequestConfiguration</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./IRequestConfiguration</span><span class="dl">"</span><span class="p">;</span>

<span class="kd">class</span> <span class="nx">UserInfo</span> <span class="p">{</span>
    <span class="nl">accountAvailable</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">;</span>
    <span class="nl">displayName</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
    <span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">displayName</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">accountAvailable</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nx">MsalHandler</span> <span class="p">{</span>
    <span class="nl">msalObj</span><span class="p">:</span> <span class="nx">UserAgentApplication</span><span class="p">;</span>
    <span class="nl">redirect</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">;</span>
    <span class="nl">useStackLogging</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">;</span>

    <span class="c1">// for handling a single instance of the handler, use getInstance() elsewhere</span>
    <span class="k">static</span> <span class="nx">instance</span><span class="p">:</span> <span class="nx">MsalHandler</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">static</span> <span class="nx">createInstance</span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MsalHandler</span><span class="p">();</span>
        <span class="k">return</span> <span class="nx">a</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">static</span> <span class="nx">getInstance</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">instance</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">instance</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">createInstance</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">instance</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// default scopes from configuration</span>
    <span class="k">private</span> <span class="nx">requestConfiguration</span><span class="p">:</span> <span class="nx">IRequestConfiguration</span> <span class="o">=</span> <span class="nx">MsalConfig</span><span class="p">.</span><span class="nx">defaultRequestConfiguration</span><span class="p">;</span>

    <span class="c1">// we want this private to prevent any external callers from directly instantiating, instead rely on getInstance()</span>
    <span class="k">private</span> <span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">redirect</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">useStackLogging</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
        <span class="kd">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">UserAgentApplication</span><span class="p">(</span><span class="nx">MsalConfig</span><span class="p">.</span><span class="nx">config</span><span class="p">);</span>

        <span class="nx">a</span><span class="p">.</span><span class="nx">handleRedirectCallback</span><span class="p">((</span><span class="nx">error</span><span class="p">,</span> <span class="nx">response</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">this</span><span class="p">.</span><span class="nx">processLogin</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
                <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">error</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">});</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">msalObj</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">async</span> <span class="nx">login</span><span class="p">(</span><span class="nx">redirect</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">,</span> <span class="nx">state</span><span class="p">?:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">scopes</span><span class="p">?:</span> <span class="kr">string</span><span class="p">[])</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">requestConfiguration</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span> <span class="na">appState</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">state</span> <span class="p">});</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">redirect</span> <span class="o">||</span> <span class="k">this</span><span class="p">.</span><span class="nx">redirect</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">msalObj</span><span class="p">.</span><span class="nx">loginRedirect</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">requestConfiguration</span><span class="p">);</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">try</span> <span class="p">{</span>
                <span class="kd">var</span> <span class="nx">response</span> <span class="o">=</span> <span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">msalObj</span><span class="p">.</span><span class="nx">loginPopup</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">requestConfiguration</span><span class="p">);</span>
                <span class="k">this</span><span class="p">.</span><span class="nx">processLogin</span><span class="p">(</span><span class="nx">response</span><span class="p">);</span>
            <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
                <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="k">async</span> <span class="nx">acquireAccessToken</span><span class="p">(</span><span class="nx">state</span><span class="p">?:</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">redirect</span><span class="p">?:</span> <span class="nx">boolean</span><span class="p">,</span> <span class="nx">scopes</span><span class="p">?:</span> <span class="kr">string</span><span class="p">[]):</span> <span class="nb">Promise</span><span class="o">&lt;</span><span class="nb">String</span> <span class="o">|</span> <span class="kc">null</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">scopes</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">requestConfiguration</span><span class="p">.</span><span class="nx">scopes</span> <span class="o">=</span> <span class="nx">scopes</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">requestConfiguration</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">({</span> <span class="na">appState</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">state</span> <span class="p">});</span>
        <span class="p">}</span>
        <span class="k">try</span> <span class="p">{</span>
            <span class="kd">var</span> <span class="nx">token</span> <span class="o">=</span> <span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">msalObj</span><span class="p">.</span><span class="nx">acquireTokenSilent</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">requestConfiguration</span><span class="p">);</span>
            <span class="k">return</span> <span class="nx">token</span><span class="p">.</span><span class="nx">accessToken</span><span class="p">;</span>
        <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">e</span> <span class="k">instanceof</span> <span class="nx">AuthError</span><span class="p">)</span> <span class="p">{</span>
                <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">"</span><span class="s2">acquireAccessToken: error: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">e</span><span class="p">));</span>
                <span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">errorCode</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">user_login_error</span><span class="dl">"</span> <span class="o">||</span> <span class="nx">e</span><span class="p">.</span><span class="nx">errorCode</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">consent_required</span><span class="dl">"</span> <span class="o">||</span> <span class="nx">e</span><span class="p">.</span><span class="nx">errorCode</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">interaction_required</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// todo: check for other error codes</span>
                    <span class="k">this</span><span class="p">.</span><span class="nx">login</span><span class="p">(</span><span class="nx">redirect</span><span class="p">,</span> <span class="nx">state</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">requestConfiguration</span><span class="p">.</span><span class="nx">scopes</span><span class="p">);</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="nx">getUserData</span><span class="p">():</span> <span class="nx">UserInfo</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">account</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">msalObj</span><span class="p">.</span><span class="nx">getAccount</span><span class="p">();</span>
        <span class="kd">var</span> <span class="nx">u</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">UserInfo</span><span class="p">();</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">account</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">u</span><span class="p">.</span><span class="nx">accountAvailable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
            <span class="nx">u</span><span class="p">.</span><span class="nx">displayName</span> <span class="o">=</span> <span class="nx">account</span><span class="p">.</span><span class="nx">name</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="nx">u</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">public</span> <span class="nx">processLogin</span><span class="p">(</span><span class="nx">response</span><span class="p">:</span> <span class="nx">AuthResponse</span> <span class="o">|</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">accountState</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">try</span> <span class="p">{</span>
                <span class="kd">var</span> <span class="nx">state</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">accountState</span><span class="p">);</span>
                <span class="k">if</span> <span class="p">(</span><span class="nx">state</span><span class="p">.</span><span class="nx">appState</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// we had a redirect from another place in the app before the authentication request</span>
                    <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">pathname</span> <span class="o">=</span> <span class="nx">state</span><span class="p">.</span><span class="nx">state</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
                <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">couldn't parse state - maybe not ours</span><span class="dl">"</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><a href="https://gist.github.com/jguadagno/56fa09b2e5498824795e3a41b2629866/raw/f5797bade7486ecd2fc5322c8a558d6606b562bc/MsalHandler.ts" target="_blank">Download</a></p>

<p>Outside of the initial changes to <code class="language-plaintext highlighter-rouge">MsalConfig.ts</code>, you shouldn’t have to change these files once you create them.</p>

<h3 id="create-the-azure-client-application">Create the Azure Client Application</h3>

<p>I’ve covered how to register/create an application in Azure before. If you haven’t done this before, check out ‘<a href="/2020/08/29/working-with-microsoft-identity-registering-an-application">Working with Microsoft Identity - Registering an Application</a>’</p>

<p>Once you created the application, update <code class="language-plaintext highlighter-rouge">Contacts\msal\MsalConfig.ts</code> with the correct client id.</p>

<h3 id="verify-the-authentication-is-working">Verify the Authentication is Working</h3>

<p>To verify that we have the authentication configuration working we are going to build a screen that will use the <code class="language-plaintext highlighter-rouge">MsalHandler</code> to interact with the Microsoft Identity library and services. Create a folder called <code class="language-plaintext highlighter-rouge">screens</code>, the folder structure is totally optional.  I typically break out the folder structure of my applications based on functionality, so screens makes sense to me. We are going to create a file called <code class="language-plaintext highlighter-rouge">Auth.tsx</code>.  This screen will serve two purposes right now, the first is to log you into the application, the second is to show you what is in the token that Microsoft Identity library is using.</p>

<p class="notice--danger"><strong><em>NOTE</em></strong> You should not use most of this code in production.  Do not show your tokens or credentials in your application</p>

<h4 id="authtsx">Auth.tsx</h4>

<p>Paste in the following contents into the newly created <code class="language-plaintext highlighter-rouge">auth.tsx</code>.</p>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
</pre></td><td class="rouge-code"><pre><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Button</span><span class="p">,</span> <span class="nx">View</span><span class="p">,</span> <span class="nx">Text</span><span class="p">,</span> <span class="nx">FlatList</span><span class="p">,</span> <span class="nx">StyleSheet</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react-native</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">MsalHandler</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../msal/MsalHandler</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kd">class</span> <span class="nx">Claim</span> <span class="p">{</span>
    <span class="nl">key</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span>
    <span class="nl">value</span><span class="p">:</span> <span class="nx">string</span><span class="p">;</span>

    <span class="kd">constructor</span><span class="p">(</span><span class="nx">key</span><span class="p">:</span> <span class="nx">string</span><span class="p">,</span> <span class="nx">value</span><span class="p">:</span> <span class="nx">string</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">key</span> <span class="o">=</span> <span class="nx">key</span><span class="p">;</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">export</span> <span class="k">default</span> <span class="kd">class</span> <span class="nx">Auth</span> <span class="kd">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
    <span class="nl">msalHandler</span><span class="p">:</span> <span class="nx">MsalHandler</span><span class="p">;</span>
    <span class="nl">accountAvailable</span><span class="p">:</span> <span class="nx">boolean</span><span class="p">;</span>

    <span class="kd">constructor</span><span class="p">(</span><span class="nx">props</span><span class="p">:</span> <span class="nx">any</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">super</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">msalHandler</span> <span class="o">=</span> <span class="nx">MsalHandler</span><span class="p">.</span><span class="nx">getInstance</span><span class="p">();</span> <span class="c1">// note this returns the previously instantiated MsalHandler</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">handleClick</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">handleClick</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">accountAvailable</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
        <span class="na">claims</span><span class="p">:</span> <span class="nb">Array</span><span class="o">&lt;</span><span class="nx">Claim</span><span class="o">&gt;</span><span class="p">(),</span>
    <span class="p">}</span>

    <span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">account</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">msalHandler</span><span class="p">.</span><span class="nx">msalObj</span><span class="p">.</span><span class="nx">getAccount</span><span class="p">();</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">account</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">accountAvailable</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">accountAvailable</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">this</span><span class="p">.</span><span class="nx">parseToken</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">msalHandler</span><span class="p">.</span><span class="nx">msalObj</span><span class="p">.</span><span class="nx">getAccount</span><span class="p">().</span><span class="nx">idToken</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span> <span class="p">}</span>
    <span class="p">}</span>

    <span class="nx">parseToken</span><span class="p">(</span><span class="nx">token</span><span class="p">:</span> <span class="nx">any</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">claimData</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">token</span><span class="p">).</span><span class="nx">filter</span><span class="p">(</span><span class="nx">y</span> <span class="o">=&gt;</span> <span class="nx">y</span> <span class="o">!==</span> <span class="dl">"</span><span class="s2">decodedIdToken</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="nx">y</span> <span class="o">!==</span> <span class="dl">"</span><span class="s2">rawIdToken</span><span class="dl">"</span><span class="p">).</span><span class="nx">map</span><span class="p">(</span><span class="nx">x</span> <span class="o">=&gt;</span> <span class="p">{</span>
            <span class="k">return</span> <span class="k">new</span> <span class="nx">Claim</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">token</span><span class="p">[</span><span class="nx">x</span><span class="p">])</span> <span class="p">?</span> <span class="nx">token</span><span class="p">[</span><span class="nx">x</span><span class="p">].</span><span class="nx">join</span><span class="p">(</span><span class="dl">"</span><span class="s2">,</span><span class="dl">"</span><span class="p">)</span> <span class="p">:</span> <span class="nx">token</span><span class="p">[</span><span class="nx">x</span><span class="p">].</span><span class="nx">toString</span><span class="p">());</span>
        <span class="p">});</span>
        <span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="na">claims</span><span class="p">:</span> <span class="nx">claimData</span> <span class="p">});</span>
    <span class="p">}</span>

    <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">accountAvailable</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="p">(</span>
                <span class="p">&lt;</span><span class="nc">View</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">container</span><span class="si">}</span><span class="p">&gt;</span>
                    <span class="p">&lt;</span><span class="nc">Text</span><span class="p">&gt;</span>User Claims<span class="p">&lt;/</span><span class="nc">Text</span><span class="p">&gt;</span>
                    <span class="p">&lt;</span><span class="nc">FlatList</span> 
                        <span class="na">data</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">claims</span><span class="si">}</span>
                        <span class="na">renderItem</span><span class="p">=</span><span class="si">{</span><span class="p">(</span><span class="nx">claimData</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span>
                            <span class="p">&lt;</span><span class="nc">View</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">row</span><span class="si">}</span><span class="p">&gt;</span>
                                <span class="p">&lt;</span><span class="nc">Text</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">column</span><span class="si">}</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">claimData</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">key</span><span class="si">}</span><span class="p">&lt;/</span><span class="nc">Text</span><span class="p">&gt;</span>
                                <span class="p">&lt;</span><span class="nc">Text</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">column</span><span class="si">}</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">claimData</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">value</span><span class="si">}</span><span class="p">&lt;/</span><span class="nc">Text</span><span class="p">&gt;</span>
                            <span class="p">&lt;/</span><span class="nc">View</span><span class="p">&gt;</span>
                        <span class="p">)</span><span class="si">}</span> <span class="p">/&gt;</span>
                <span class="p">&lt;/</span><span class="nc">View</span><span class="p">&gt;</span>
            <span class="p">)</span>
        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
            <span class="k">return</span> <span class="p">(</span>
                <span class="p">&lt;</span><span class="nc">View</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">container</span><span class="si">}</span><span class="p">&gt;</span>
                    <span class="p">&lt;</span><span class="nc">Button</span> <span class="na">onPress</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">handleClick</span><span class="si">}</span> <span class="na">title</span><span class="p">=</span><span class="s">"Login"</span> <span class="p">/&gt;</span>
                <span class="p">&lt;/</span><span class="nc">View</span><span class="p">&gt;</span>
            <span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">async</span> <span class="nx">handleClick</span><span class="p">(</span><span class="nx">e</span><span class="p">:</span> <span class="nx">any</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">clicked</span><span class="dl">"</span><span class="p">);</span>
        <span class="k">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">msalHandler</span><span class="p">.</span><span class="nx">login</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">styles</span> <span class="o">=</span> <span class="nx">StyleSheet</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
    <span class="na">container</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">flex</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
        <span class="na">backgroundColor</span><span class="p">:</span> <span class="dl">'</span><span class="s1">white</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">justifyContent</span><span class="p">:</span> <span class="dl">'</span><span class="s1">center</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">flexDirection</span><span class="p">:</span> <span class="dl">'</span><span class="s1">row</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">flexWrap</span><span class="p">:</span> <span class="dl">'</span><span class="s1">wrap</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">alignItems</span><span class="p">:</span> <span class="dl">'</span><span class="s1">flex-start</span><span class="dl">'</span>
    <span class="p">},</span>
    <span class="na">row</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">display</span><span class="p">:</span> <span class="dl">'</span><span class="s1">flex</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">flexDirection</span><span class="p">:</span> <span class="dl">'</span><span class="s1">row</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">flexWrap</span><span class="p">:</span> <span class="dl">'</span><span class="s1">wrap</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">width</span><span class="p">:</span> <span class="dl">'</span><span class="s1">100%</span><span class="dl">'</span>
    <span class="p">},</span>
    <span class="na">column</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">display</span><span class="p">:</span> <span class="dl">'</span><span class="s1">flex</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">flexDirection</span><span class="p">:</span> <span class="dl">'</span><span class="s1">column</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">flexBasis</span><span class="p">:</span> <span class="dl">'</span><span class="s1">50%</span><span class="dl">'</span><span class="p">,</span>
        <span class="na">flex</span><span class="p">:</span> <span class="mi">1</span>
    <span class="p">}</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now, I’m not going to explain every line of the file just highlight the parts that are important to the authentication.</p>

<p>Line 3, imports the <code class="language-plaintext highlighter-rouge">MsalHandler</code> so it is available to this screen.</p>

<p>Line 5, declares a <code class="language-plaintext highlighter-rouge">Claim</code> class which is used to display the claims.  This is not needed for authentication but helpful while you are troubleshooting.</p>

<p class="notice--danger"><strong><em>Note</em></strong> Do not include the <code class="language-plaintext highlighter-rouge">Claim</code> class in the production version of your application.</p>

<p>Line 21, we get an instance of the <code class="language-plaintext highlighter-rouge">MsalHandler</code>.</p>

<p>Now in the <code class="language-plaintext highlighter-rouge">componentDidMount()</code> function, lines 30-39, we attempt to get the account for the current user via a call to  <code class="language-plaintext highlighter-rouge">msalHandler.msalObj.getAccount()</code> on line 31. If account is not <code class="language-plaintext highlighter-rouge">undefined</code>, we set the local variable <code class="language-plaintext highlighter-rouge">accountAvailable</code> equal to <code class="language-plaintext highlighter-rouge">true</code>.</p>

<p>The <code class="language-plaintext highlighter-rouge">parseToken</code> function, lines 41-46, are used to split the token into a key-value pair for display.  Again, this is for debugging and testing purposes. <strong>DO NOT</strong> include this code in your production application.</p>

<p>The <code class="language-plaintext highlighter-rouge">render</code> method handles the display logic which differs if the user is logged in or not.</p>

<p>The <code class="language-plaintext highlighter-rouge">handleClick</code> function, lines 72-76, performs the login by executing the <code class="language-plaintext highlighter-rouge">msalHandler.login()</code> method.</p>

<h4 id="apptsx">App.tsx</h4>

<p>Now let us update the application to call the authentication page. Replace the contents of <code class="language-plaintext highlighter-rouge">App.tsx</code> with the following:</p>

<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
</pre></td><td class="rouge-code"><pre><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">Button</span><span class="p">,</span> <span class="nx">StyleSheet</span><span class="p">,</span> <span class="nx">Text</span><span class="p">,</span> <span class="nx">View</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react-native</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">Api</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./api</span><span class="dl">'</span>

<span class="k">import</span> <span class="p">{</span> <span class="nx">NavigationContainer</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@react-navigation/native</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">createStackNavigator</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@react-navigation/stack</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">Auth</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./screens/Auth</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">MsalHandler</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./msal/MsalHandler</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">msal</span> <span class="o">=</span> <span class="nx">MsalHandler</span><span class="p">.</span><span class="nx">getInstance</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">user</span> <span class="o">=</span> <span class="nx">msal</span><span class="p">.</span><span class="nx">getUserData</span><span class="p">();</span>

<span class="kd">function</span> <span class="nx">HomeScreen</span><span class="p">({</span><span class="nx">navigation</span><span class="p">})</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span>
    <span class="p">&lt;</span><span class="nc">View</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">container</span><span class="si">}</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nc">Text</span><span class="p">&gt;</span>Welcome new followers!<span class="p">&lt;/</span><span class="nc">Text</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nc">Button</span> <span class="na">title</span><span class="p">=</span><span class="s">"Hello"</span> <span class="na">onPress</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello</span><span class="dl">"</span><span class="p">);}</span><span class="si">}</span> <span class="p">/&gt;</span>
      <span class="p">&lt;</span><span class="nc">Button</span> <span class="na">title</span><span class="p">=</span><span class="si">{</span><span class="nx">user</span><span class="p">.</span><span class="nx">accountAvailable</span> <span class="p">?</span> <span class="dl">"</span><span class="s2">Claims for </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">user</span><span class="p">.</span><span class="nx">displayName</span> <span class="p">:</span> <span class="dl">"</span><span class="s2">Login</span><span class="dl">"</span><span class="si">}</span> <span class="na">onPress</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">navigation</span><span class="p">.</span><span class="nx">navigate</span><span class="p">(</span><span class="dl">'</span><span class="s1">Auth</span><span class="dl">'</span><span class="p">)</span><span class="si">}</span> <span class="p">/&gt;</span>
    <span class="p">&lt;/</span><span class="nc">View</span><span class="p">&gt;</span>
  <span class="p">);</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">Stack</span> <span class="o">=</span> <span class="nx">createStackNavigator</span><span class="p">();</span>

<span class="k">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span>
    <span class="p">&lt;</span><span class="nc">NavigationContainer</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nc">Stack</span><span class="p">.</span><span class="nc">Navigator</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nc">Stack</span><span class="p">.</span><span class="nc">Screen</span> <span class="na">name</span><span class="p">=</span><span class="s">"Home"</span> <span class="na">component</span><span class="p">=</span><span class="si">{</span><span class="nx">HomeScreen</span><span class="si">}</span> <span class="p">/&gt;</span>
      <span class="p">&lt;</span><span class="nc">Stack</span><span class="p">.</span><span class="nc">Screen</span> <span class="na">name</span><span class="p">=</span><span class="s">"Auth"</span> <span class="na">component</span><span class="p">=</span><span class="si">{</span><span class="nx">Auth</span><span class="si">}</span> <span class="p">/&gt;</span>
    <span class="p">&lt;/</span><span class="nc">Stack</span><span class="p">.</span><span class="nc">Navigator</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nc">NavigationContainer</span><span class="p">&gt;</span>
  <span class="p">);</span>
<span class="p">}</span>

<span class="kd">const</span> <span class="nx">styles</span> <span class="o">=</span> <span class="nx">StyleSheet</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
  <span class="na">container</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">flex</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
    <span class="na">backgroundColor</span><span class="p">:</span> <span class="dl">'</span><span class="s1">white</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">alignItems</span><span class="p">:</span> <span class="dl">'</span><span class="s1">center</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">justifyContent</span><span class="p">:</span> <span class="dl">'</span><span class="s1">center</span><span class="dl">'</span><span class="p">,</span>
  <span class="p">},</span>
<span class="p">});</span>

</pre></td></tr></tbody></table></code></pre></div></div>

<p>Again, I’m not going to explain the whole file just the parts related to the authentication.</p>

<p>Line 7 and 8, we import the new Auth screen and the <code class="language-plaintext highlighter-rouge">MsalHandler</code> library.</p>

<p>Line 10, we create an instance of the <code class="language-plaintext highlighter-rouge">MsalHandler</code>.</p>

<p>Line 11, we attempt to get the user data from <code class="language-plaintext highlighter-rouge">getUserData</code> function of the <code class="language-plaintext highlighter-rouge">MsalHandler</code>. The call checks to see if there if the user has authenticated and has an access token already.</p>

<p>Line 18, determines whether to display the link to the authentication page or the user information.  If the user is already authenticated, the button will change to the users’ name, in my case, the button will be titled <em>Claims for Joseph Guadagno</em>, if the user is not logged in or authenticated, the button will display <em>Login</em>.</p>

<h4 id="test-the-authentication">Test the Authentication</h4>

<p>Start the application.  Execute the following command from the console.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>yarn web
</pre></td></tr></tbody></table></code></pre></div></div>

<p>If this is the first time you are running this application with this client id, or the first time running the application with the assigned client id with the current user, Azure Active Directory will prompt you to log in and accept the permissions for the application.  In our sample, we are asking to call the APIs on behalf of the signed-in user. If prompted,</p>

<ul>
  <li>Click on the ‘Login’ button</li>
  <li>Click the next ‘Login’ button</li>
  <li>Login, accept permissions</li>
</ul>

<p>If the login was successful, the login button should change to <em>Claims for …</em> where the … is your name.</p>

<p>If you click on the <em>Claims for …</em> button, you will the claims that was sent back.</p>

<h3 id="call-the-api">Call the API</h3>

<p>Now that we verified the API and application authentication works, we are going to need to update the API client to add the required headers for authentication and change the base URL.</p>

<h4 id="indexjs">index.js</h4>

<p>Open up <code class="language-plaintext highlighter-rouge">Contacts/api/index.js</code> and replace the contents with the following:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="rouge-code"><pre><span class="k">import</span> <span class="nx">Axios</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">axios</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">MsalHandler</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../msal/MsalHandler</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">ContactsApi</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./generated</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">msalHandler</span> <span class="o">=</span> <span class="nx">MsalHandler</span><span class="p">.</span><span class="nx">getInstance</span><span class="p">();</span>

<span class="kd">const</span> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">Axios</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span><span class="na">baseURL</span><span class="p">:</span> <span class="dl">'</span><span class="s1">https://localhost:5001</span><span class="dl">'</span><span class="p">});</span>
<span class="nx">instance</span><span class="p">.</span><span class="nx">interceptors</span><span class="p">.</span><span class="nx">request</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span>
    <span class="k">async</span> <span class="nx">request</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">token</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">msalHandler</span><span class="p">.</span><span class="nx">acquireAccessToken</span><span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">url</span><span class="p">);</span>
        <span class="nx">request</span><span class="p">.</span><span class="nx">headers</span><span class="p">[</span><span class="dl">"</span><span class="s2">Authorization</span><span class="dl">"</span><span class="p">]</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Bearer </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">token</span><span class="p">;</span>
        <span class="k">return</span> <span class="nx">request</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">);</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
    <span class="na">Contacts</span><span class="p">:</span> <span class="k">new</span> <span class="nx">ContactsApi</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="dl">'</span><span class="s1">https://localhost:5001</span><span class="dl">'</span><span class="p">,</span> <span class="nx">instance</span><span class="p">)</span>
<span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>As you can see, this is the first name where we are using the Axios library and this is the primary reason for using the Axios library and not the native React Native fetch function.  Axios provides us with the ability to <em>intercept</em> requests.  We want/need to intercept the request so we can add the required authentication header bearer token.</p>

<p>Line 5, we get an instance of the <code class="language-plaintext highlighter-rouge">MsalHandler</code>.</p>

<p>Line 7, we create an instance of the Axios library for the URL of <code class="language-plaintext highlighter-rouge">https://localhost:5001</code>.</p>

<p>Lines 8-14, we create an interceptor for any request to <code class="language-plaintext highlighter-rouge">https://localhost:5001</code>.  This interceptor will inject the token given from the Microsoft Identity library (line 10) call to <code class="language-plaintext highlighter-rouge">acquireAccessToken</code> and create a new <em>authorization</em> header with the bearer token, line 11;</p>

<p>Line 17, changes the initial creation of our API client to use the base URL of <code class="language-plaintext highlighter-rouge">localhost:5001</code> and the instance of Axios created on line 7.</p>

<p class="notice--info"><strong><em>NOTE</em></strong> You’ll want to change the URLs to your production URLs</p>

<h4 id="apptsx-1">app.tsx</h4>

<p>Now that the API client has been updated let’s add a new button to the application so that we can make some APIs call to verify the authentication is working.</p>

<p>Open up the <code class="language-plaintext highlighter-rouge">App.tsx</code> file and add the following button.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre>    <span class="o">&lt;</span><span class="nx">Button</span> <span class="nx">title</span><span class="o">=</span><span class="dl">"</span><span class="s2">Number of Contacts</span><span class="dl">"</span> <span class="nx">onPress</span><span class="o">=</span><span class="p">{</span><span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">contactList</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Api</span><span class="p">.</span><span class="nx">Contacts</span><span class="p">.</span><span class="nx">contactsGet</span><span class="p">();</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Number of contacts: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">contactList</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span>

        <span class="kd">var</span> <span class="nx">contact</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">Api</span><span class="p">.</span><span class="nx">Contacts</span><span class="p">.</span><span class="nx">contactsIdGet</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
        <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">First Contact Name: '</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">contact</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">fullName</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">'</span><span class="dl">"</span><span class="p">);</span>
      <span class="p">}}</span> <span class="sr">/</span><span class="err">&gt;
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>If the application is not already started, start it.  Then:</p>

<ul>
  <li>Open up the developer tools of your browser</li>
  <li>Click the <em>Console</em> tabs</li>
  <li>Click on the number of Contacts</li>
</ul>

<p>Now, if you had access to the API, you would have seen <em>Number of contacts: 5</em> and <em>First Contact Name: ‘Joseph Guadagno’</em> in the developer tools console.</p>

<h2 id="wrap-up">Wrap Up</h2>

<p>Wow, that was a lot.  As you can see, the initial setup is a little challenging but once it is done there is nothing you really need to do except for build your application.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://reactnative.dev" target="_blank">React Native</a></li>
  <li>OpenAPI Tools <a href="https://github.com/OpenAPITools/openapi-generator" target="_blank">OpenAPI Generator</a></li>
  <li><a href="https://medium.com/tribalscale/how-to-automate-api-code-generation-openapi-swagger-and-boost-productivity-1176a0056d8a">How to automate API code generation (OpenAPI/Swagger) and boost productivity</a></li>
  <li><a href="https://upmostly.com/tutorials/using-axios-with-react-api-requests" target="_blank">Using Axios with React to Make API Requests</a></li>
  <li><a href="https://microsoftedge.microsoft.com/addons/detail/react-developer-tools/gpphkfbcpidddadnkolkpfckpihlkkil" target="_blank">React Native Dev Tools</a></li>
  <li>JP Dandison’s, aka <a href="https://www.twitter.com/AzureAndChill" target="_blank">@AzureAndChill</a> <a href="https://github.com/jpda/msaljs-axios-intercept" target="_blank">Axios Intercept</a> with Microsoft Identity Library for JavaScript <a href="https://github.com/AzureAD/microsoft-authentication-library-for-js" target="_blank">MSAL.js</a></li>
  <li>Video: <a href="https://youtu.be/IScDA7cKsSM" target="_blank">Introduction to React Native</a></li>
  <li>Video: <a href="https://youtu.be/9Wot8p9bWf4" target="_blank">Building the API Client and Authentication</a></li>
  <li>Completed code repository at <a href="https://github.com/jguadagno/contacts-react-native-client" target="_blank">https://github.com/jguadagno/contacts-react-native-client</a></li>
  <li></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Identity" /><category term="MSAL" /><category term="Managed Identity" /><category term="React Native" /><category term="Entra" /><summary type="html"><![CDATA[In this post, I’m going to walk through how you authenticate and use an API that is secured with Azure Active Directory using React Native and the Microsoft Identity library.]]></summary></entry><entry><title type="html">Priorities</title><link href="https://www.josephguadagno.net/2020/09/19/priorities" rel="alternate" type="text/html" title="Priorities" /><published>2020-09-19T18:50:00-07:00</published><updated>2020-09-19T18:50:00-07:00</updated><id>https://www.josephguadagno.net/2020/09/19/priorities</id><content type="html" xml:base="https://www.josephguadagno.net/2020/09/19/priorities"><![CDATA[<p>A year ago, yesterday, I went to the Emergency Room because I was getting cold sweats, and something did not feel right with my body. Well, after a few tests and a few doctors later, a cardiologist came in and told me I had 90% blockage in the <em><a href="https://en.wikipedia.org/wiki/Left_anterior_descending_artery">left anterior descending artery</a></em>, better known as the <em><a href="https://www.webmd.com/heart-disease/widow-maker-heart-attack">Widow Maker</a></em>. It’s known as the <em>widow maker</em> because you tend to have minutes left to live when it is blocked. I am glad I listened to my body and my wife and went into the Emergency Room.</p>

<p>A year ago today, the cardiologist successfully cleared the blockage and got me on the path to a healthier life.</p>

<h2 id="what-got-me-there">What Got Me There</h2>

<p>There a quite a few factors that contribute to clogged arteries and/or being out of shape.  Here are some of the common:</p>

<ul>
  <li>:black_square_button: Smoke</li>
  <li>:white_square_button: Are obese</li>
  <li>:white_square_button: Eat a lot of unhealthy foods</li>
  <li>:white_square_button: Are over 45 (men) or 55 (women)</li>
  <li>:white_square_button: Don’t exercise</li>
  <li>:white_square_button: Have high blood pressure</li>
  <li>:white_square_button: Have uncontrolled diabetes</li>
  <li>:white_square_button: Have a family history of heart disease</li>
</ul>

<p>Looking at the list, I checked off seven of the eight.</p>

<dl>
  <dt>I was obese.</dt>
  <dd>I weighed 243lbs/110kg. I was an XXL shirt and 44 waist size for pants</dd>
  <dt>I ate like crap.</dt>
  <dd>Airline food, junk food, candy, beer/vodka, and more</dd>
  <dt>Are over 45</dt>
  <dd>Yep</dd>
  <dt>Don’t exercise</dt>
  <dd>The occasional laps around the airport or conference venue halls</dd>
  <dt>High Blood Pressure</dt>
  <dd>I think it was 220/140 when I checked in to the hospital.  I was really high</dd>
  <dt>Uncontrolled diabetes</dt>
  <dd>My blood sugar was upwards of 330</dd>
  <dt>A family history of heart disease</dt>
  <dd>Yep</dd>
</dl>

<p>These check marks :white_square_button: are not something I am not proud of. I am quite disappointed :disappointed: in myself for letting myself get there. I know I could have gone with the excuse that I was doing a lot of traveling with work, public speaking, I was up late with work, preparing presentations, etc., the list could go on. But those are just excuses! I let myself go.
Those that know me to know I have a few mottos or things I live by. One of my favorites is</p>

<p>Those that know me, know that I have a few mottos, or things I live by. One of my favorites is</p>

<blockquote>
  <p><em>I work to live, I don’t live to work</em>!.</p>
</blockquote>

<p>Somewhere along the way, I lost sight of that, and I guess I needed this <strong><em>wake-up call</em></strong>.</p>

<h2 id="me-now">Me Now</h2>

<p>Thanks to the proverbial kick in the pants, and some medication, I am back on track. I watch what I eat now. I have five smaller meals a day that are heart-healthy, including vegetables, fruits, etc. I’ve avoided fried foods, foods high in salt, and most sweets. I’ve stopped drinking alcohol. I try to get around 15,000 steps a day now, which is really hard to be stuck at home.</p>

<p>You’ll notice I used the word to avoid. I did not eliminate fried foods, sweets, etc. I just have them way less frequently. The reason being is you still have to live, and cutting everything out usually leads to failure.</p>

<p>Looking at the previous list</p>

<ul>
  <li>:black_square_button: Smoke</li>
  <li>:black_square_button: Are obese</li>
  <li>:black_square_button: Eat a lot of unhealthy foods</li>
  <li>:white_square_button: Are over 45 (men) or 55 (women)</li>
  <li>:black_square_button: Don’t exercise</li>
  <li>:black_square_button: Have high blood pressure</li>
  <li>:black_square_button: Have uncontrolled diabetes</li>
  <li>:white_square_button: Have a family history of heart disease</li>
</ul>

<p>Looking at the list, I now have two of the eight checked.  Can’t change my age or family health history.</p>

<dl>
  <dt>I am now longer obese.</dt>
  <dd>I weighed 193lbs/87kg. I was an L shirt and 36 waist size for pants</dd>
  <dt>I eat healthier.</dt>
  <dd>I eat heart-healthy meals consisting of green, leafy vegetable, lean meats, etc..  I control my meals now.</dd>
  <dt>Are over 45</dt>
  <dd>Yep</dd>
  <dt>Exercise</dt>
  <dd>I try to get in 15,000 steps a day or a mile on the treadmill.</dd>
  <dt>High Blood Pressure</dt>
  <dd>I average around 110/70 now</dd>
  <dt>Uncontrolled diabetics</dt>
  <dd>My blood sugar averages around 90 before dinner</dd>
  <dt>A family history of heart disease</dt>
  <dd>Yep</dd>
</dl>

<h2 id="the-morale-of-the-story">The Morale of the Story</h2>

<p>Times are hard now, more than ever, for most of us.  You must take a step back and look at your life and situation to see what decisions you are making, and they impact yourself and those around you.  Remember, you could not only be affecting yourself both those around you.</p>

<p>You, too, can work to live and not live to work.</p>

<p>This event, a year ago, was my reminder that…</p>

<p class="notice--info">I work to live. I don’t live to work!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Personal" /><summary type="html"><![CDATA[A year ago, yesterday, I went to the Emergency Room because I was getting cold sweats, and something did not feel right with my body. Well, after a few tests and a few doctors later, a cardiologist came in and told me I had 90% blockage in the left anterior descending artery, better known as the Widow Maker. It’s known as the widow maker because you tend to have minutes left to live when it is blocked. I am glad I listened to my body and my wife and went into the Emergency Room.]]></summary></entry><entry><title type="html">Dependency Injection with Azure Functions</title><link href="https://www.josephguadagno.net/2020/09/06/dependency-injection-wth-azure-functions" rel="alternate" type="text/html" title="Dependency Injection with Azure Functions" /><published>2020-09-06T18:50:00-07:00</published><updated>2020-11-06T16:16:00-07:00</updated><id>https://www.josephguadagno.net/2020/09/06/dependency-injection-wth-azure-functions</id><content type="html" xml:base="https://www.josephguadagno.net/2020/09/06/dependency-injection-wth-azure-functions"><![CDATA[<p>I got bit by the dependency injection bug a few months ago and want to use it when I can, and it makes sense. Within a month or so of writing this post, Azure Functions started to support dependency injection in functions. It uses the .NET Core Dependency Injection system, so if you are used to that, this should mostly look familiar to you, except for what you have to do to wire up the Azure Functions SDK.</p>

<h2 id="getting-started">Getting Started</h2>

<p>To get started, you need to add a reference to <code class="language-plaintext highlighter-rouge">Microsoft.Azure.Functions.Extensions</code> package in your Azure Functions project.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>Install-Package Microsoft.Azure.Functions.Extensions <span class="nt">-Version</span> 1.0.0
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now you need a class to register your services.  I use <code class="language-plaintext highlighter-rouge">Startup</code> to be consistent with ASP.NET Core.  You can create whatever class name you want.</p>

<p>In that class, you need to inform the Azure Function SDK that this class is the startup class. To do this, add the assembly attribute <code class="language-plaintext highlighter-rouge">FunctionsStartup</code> to the class file.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="n">assembly</span><span class="p">:</span> <span class="nf">FunctionsStartup</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">Startup</span><span class="p">))]</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This will require the following using statement.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">using</span> <span class="nn">Microsoft.Azure.Functions.Extensions.DependencyInjection</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Have your class inherit from <code class="language-plaintext highlighter-rouge">FunctionsStartup</code>. Doing so requires your class to override the <code class="language-plaintext highlighter-rouge">Configure</code> method. The <code class="language-plaintext highlighter-rouge">Configure</code> method is where you register the services that you would like to inject. Since Azure Functions relies on the .NET Core Dependency Injection features, you can choose any supported <a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection?WT.mc_id=DT-MVP-4024623#service-lifetimes" target="_blank">service lifetimes</a>.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="k">using</span> <span class="nn">MyFunctions.Samples</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Azure.Functions.Extensions.DependencyInjection</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">Microsoft.Extensions.DependencyInjection.Extensions</span><span class="p">;</span>

<span class="p">[</span><span class="n">assembly</span><span class="p">:</span> <span class="nf">FunctionsStartup</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">Startup</span><span class="p">))]</span>
<span class="k">namespace</span> <span class="nn">MyFunctions.Samples</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">class</span> <span class="nc">Startup</span><span class="p">:</span> <span class="n">FunctionsStartup</span>
    <span class="p">{</span>
        <span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IFunctionsHostBuilder</span> <span class="n">builder</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">TryAddSingleton</span><span class="p">&lt;</span><span class="n">ISettings</span><span class="p">&gt;(</span><span class="n">s</span> <span class="p">=&gt;</span> <span class="k">new</span> <span class="nf">Settings</span><span class="p">());</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In this example, I am registering the <code class="language-plaintext highlighter-rouge">ISettings</code> class as a singleton class within the dependency injection system.</p>

<p>Now navigate to the class with your function.</p>

<p class="notice--info"><strong><em>NOTE</em></strong> You’ll have to remove the <code class="language-plaintext highlighter-rouge">static</code> modifier from the class and your function.</p>

<p>At this point, you can use directly inject the registered services.</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">private</span> <span class="k">readonly</span> <span class="n">ISettings</span> <span class="n">_settings</span><span class="p">;</span>

<span class="c1">// Class Constructor</span>
<span class="k">public</span> <span class="nf">MyFunctionApplication</span><span class="p">(</span><span class="n">ISettings</span> <span class="n">settings</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">_settings</span> <span class="p">=</span> <span class="n">settings</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then you can use <code class="language-plaintext highlighter-rouge">_settings</code> anywhere in your code. Like this example:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">log</span><span class="p">.</span><span class="nf">LogInformation</span><span class="p">(</span><span class="s">$"Value from Settings class='</span><span class="p">{</span><span class="n">_settings</span><span class="p">.</span><span class="n">MySetting</span><span class="p">}</span><span class="s">'"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="wrap-up">Wrap Up</h2>

<p>That’s it. Pretty easy. I’ve included links to the Azure Function documentation and a sample repository that I used it below.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection?WT.mc_id=DT-MVP-4024623" target="_blank">Use dependency injection in .NET Azure Functions</a></li>
  <li>Sample code before Dependency Injection: <a href="https://github.com/jguadagno/Contacts/blob/28349f06d2ead5282381895feb975b2b1d6a4171/src/Contacts.Functions.ThumbnailCreator/CreateThumbnailImage.cs" target="_blank">Code</a></li>
  <li>Sample code after Dependency Injection: <a href="https://github.com/jguadagno/Contacts/blob/main/src/Contacts.Functions.ThumbnailCreator/CreateThumbnailImage.cs" target="_blank">Code</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Dependency Injection" /><category term="Functions" /><summary type="html"><![CDATA[I got bit by the dependency injection bug a few months ago and want to use it when I can, and it makes sense. Within a month or so of writing this post, Azure Functions started to support dependency injection in functions. It uses the .NET Core Dependency Injection system, so if you are used to that, this should mostly look familiar to you, except for what you have to do to wire up the Azure Functions SDK.]]></summary></entry><entry><title type="html">Desert Code Camp 2020 is Cancelled</title><link href="https://www.josephguadagno.net/2020/08/29/desert-code-camp-2020-is-cancelled" rel="alternate" type="text/html" title="Desert Code Camp 2020 is Cancelled" /><published>2020-08-29T00:00:00-07:00</published><updated>2020-08-29T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/08/29/desert-code-camp-2020-is-cancelled</id><content type="html" xml:base="https://www.josephguadagno.net/2020/08/29/desert-code-camp-2020-is-cancelled"><![CDATA[<p>With deep regret, I inform you that we will not be having a Desert Code Camp this year.  There are several reasons for this.  The primary reason is the facility is not available due to the pandemic.  You may be thinking, “But what about going virtual?” I’ve thought about that, but the beauty of Desert Code Camp is the excellent networking opportunities of an in-person event, which you really can’t get virtually.</p>

<p>The good news is the learning does not need to stop! There are plenty of virtual conferences happening everywhere; many of them are free.  A lot of speakers have started live code coding like me <a href="https://jjg.me/stream" target="_blank">https://jjg.me/stream</a>.</p>

<p>I’ll try and get Desert Code Camp kicked off when it is safe to do so.</p>

<p>I hope you understand.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="News" /><category term="Arizona Technology News" /><category term="Archive" /><category term="Desert Code Camp" /><summary type="html"><![CDATA[With deep regret, I inform you that we will not be having a Desert Code Camp this year. There are several reasons for this. The primary reason is the facility is not available due to the pandemic. You may be thinking, “But what about going virtual?” I’ve thought about that, but the beauty of Desert Code Camp is the excellent networking opportunities of an in-person event, which you really can’t get virtually.]]></summary></entry><entry><title type="html">Working with Microsoft Identity - Assigning a Role</title><link href="https://www.josephguadagno.net/2020/08/29/working-with-microsoft-identity-assigning-a-role" rel="alternate" type="text/html" title="Working with Microsoft Identity - Assigning a Role" /><published>2020-08-29T00:00:00-07:00</published><updated>2020-08-29T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/08/29/working-with-microsoft-identity-assigning-a-role</id><content type="html" xml:base="https://www.josephguadagno.net/2020/08/29/working-with-microsoft-identity-assigning-a-role"><![CDATA[<p>Securing our applications and data is critical in this day and age.  I’ve been working a lot with the new <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/?wt.mc_id=DT-MVP-4024623" target="_blank">Microsoft identity platform</a>  (MSAL) library, so I decided to create a series of blog posts around working with it.</p>

<ul>
  <li><a href="/2020/08/29/working-with-microsoft-identity-registering-an-application">Register an application</a>.</li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-configure-local-development">Configure Local Development</a></li>
  <li>Assigning a Role (this post)</li>
</ul>

<h2 id="role-based-access-control">Role-Based Access Control</h2>

<p>Before we assign a role, we should take a look out what <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/overview?wt.mc_id=DT-MVP-4024623" target="_blank">Azure RBAC</a> is. Azure RBAC, or Azure Role-Based Access Control, is an authorization system built on Azure Resource Manager that provides fine-grained access management of Azure resources.  It allows you to create roles or use predefined roles for your applications.</p>

<p>Azure RBAC includes several <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles?wt.mc_id=DT-MVP-4024623" target="_blank">built-in roles</a> that you can use. The following lists four built-in roles. The first three apply to all resource types.</p>

<ul>
  <li><strong>Owner</strong> - Has full access to all resources including the right to delegate access to others.</li>
  <li><strong>Contributor</strong> - Can create and manage all types of Azure resources but can’t grant access to others.</li>
  <li><strong>Reader</strong> - Can view existing Azure resources.</li>
  <li><strong>User Access Administrator</strong> - Lets you manage user access to Azure resources.</li>
</ul>

<p>If you don’t find a role that fits your needs, you can create <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/custom-roles?wt.mc_id=DT-MVP-4024623" target="_blank">custom roles</a>. From what I have found, the default roles are adequate for my use.</p>

<h2 id="assigning-a-role">Assigning a Role</h2>

<p>Assigning a role to an application assigns a set of permissions to the Azure resource for the given application.  In the sample below, we are going to assign the <code class="language-plaintext highlighter-rouge">Storage Blob Data Contributor</code> role to our application.</p>

<p>In the Azure Portal, navigate to the resource that you want to provide access to and click on ‘Access control (IAM’) on the left menu.</p>

<p>There are two ways to add the role.</p>

<ul>
  <li>Option 1, is click the ‘+ Add’, then ‘Add role assignment’.</li>
  <li>Option 2, is to click the ‘Add’ button.</li>
</ul>

<p><img src="/assets/images/posts/securing-container-add-role.png" alt="Add IAM Role" class="align-center" /></p>

<p>Enter the following:</p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Role</td>
      <td><code class="language-plaintext highlighter-rouge">Storage Blob Data Contributor</code></td>
      <td>Add what makes sense for your application.  Not sure what setting to use, hover over the ‘i’ or check out the permissions mentioned earlier in this post.</td>
    </tr>
    <tr>
      <td>Assign access to</td>
      <td><code class="language-plaintext highlighter-rouge">Azure AD user, group, or service principal</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Select</td>
      <td><em>The name of the application</em></td>
      <td>The default will be your user id.  Type in the first couple of characters of the application</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/securing-container-add-role-assignment.png" alt="Add Role Assignment" class="align-center" /></p>

<ul>
  <li>Click ‘Save’</li>
</ul>

<h2 id="verifying-role-access">Verifying Role Access</h2>

<h3 id="check-application-access">Check Application Access</h3>

<p>If you want to check what applications/users have access to a given resource</p>

<ul>
  <li>Navigate to the resource</li>
  <li>Click ‘Access control (IAM)’</li>
  <li>Click ‘Check Access’</li>
</ul>

<p><img src="/assets/images/posts/msal-check-access.png" alt="Check Access" class="align-center" /></p>

<p>Underneath ‘Find’, choose the type of managed identity you are want to check.</p>

<p><img src="/assets/images/posts/msal-find-list.png" alt="MSAL - Identity Types" class="align-center" /></p>

<p>The default of <code class="language-plaintext highlighter-rouge">Azure AD user, group, or service principal</code> should be enough.  If you have a lot of resources, you can narrow the search results down by choosing another identity type.</p>

<ul>
  <li>Type the first couple of characters of the application and/or resource.</li>
</ul>

<p>Once you see the resource you are wanting to check roles on, click it and you will see any permissions assigned.  In this example, there were no permissions assigned.</p>

<p><img src="/assets/images/posts/msal-no-permissions-assigned.png" alt="MSAL - No Permissions Assigned" class="align-center" /></p>

<h3 id="view-all-roles-assigned">View All Roles Assigned</h3>

<p>You can view all of the roles assigned to a given resource in Azure.</p>

<ul>
  <li>Navigate to the resource</li>
  <li>Click ‘Access control (IAM)’</li>
  <li>Click ‘Role assignments’</li>
</ul>

<p>This will list all of the registered applications and/or users that have access to this application.</p>

<p><img src="/assets/images/posts/msal-role-assignments.png" alt="MSAL - Roles Assigned" class="align-center" /></p>

<p>The number 1 on the image tells us how many roles we have assigned in our subscription, not for this resource.</p>

<p>The number 2 on the image, provides you the ability to narrow down the results.  In this case, I have it filtered to those applications/users that have the <code class="language-plaintext highlighter-rouge">Storage Blob Data Contributor</code> role.</p>

<p>The number 3 on this image, lists all of the applications/users that match the filters above.</p>

<h2 id="resources">Resources</h2>

<ul>
  <li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal?wt.mc_id=DT-MVP-4024623" target="_blank">Add/Remove Azure role assignments using the Azure portal</a></li>
  <li><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/role-assignments-cli?wt.mc_id=DT-MVP-4024623" target="_blank">Add or remove Azure role assignments using Azure CLI</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Identity" /><category term="MSAL" /><category term="Managed Identity" /><category term="Entra" /><summary type="html"><![CDATA[Securing our applications and data is critical in this day and age. I’ve been working a lot with the new Microsoft identity platform (MSAL) library, so I decided to create a series of blog posts around working with it.]]></summary></entry><entry><title type="html">Working with Microsoft Identity - Configure Local Development</title><link href="https://www.josephguadagno.net/2020/08/29/working-with-microsoft-identity-configure-local-development" rel="alternate" type="text/html" title="Working with Microsoft Identity - Configure Local Development" /><published>2020-08-29T00:00:00-07:00</published><updated>2020-08-29T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/08/29/working-with-microsoft-identity-configure-local-development</id><content type="html" xml:base="https://www.josephguadagno.net/2020/08/29/working-with-microsoft-identity-configure-local-development"><![CDATA[<p>Securing our applications and data is critical in this day and age.  I’ve been working a lot with the new <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/?wt.mc_id=DT-MVP-4024623" target="_blank">Microsoft identity platform</a>  (MSAL) library, so I decided to create a series of blog posts around working with it.</p>

<ul>
  <li><a href="/2020/08/29/working-with-microsoft-identity-registering-an-application">Register an application</a>.</li>
  <li>Configure Local Development (this post)</li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-assigning-a-role">Assigning a Role</a></li>
</ul>

<h2 id="using-environment-variable">Using Environment Variable</h2>

<p>Your setup may vary depending on the IDE you are using, Visual Studio, JetBrains Rider, IntelliJ, Visual Studio Code, etc.  I’m going to show you how to set up your <em>Environment</em> variables to use the <code class="language-plaintext highlighter-rouge">DefaultAzureCredentials</code>.  For this, you will need the Application (Client) ID, Directory (Tenant) ID, and Client Secret (password) obtained from registering your application with the Azure portal.  If you need to register an application, check out the post <a href="/2020/08/29/working-with-microsoft-identity-registering-an-application">Register an application</a>.</p>

<h3 id="windows">Windows</h3>

<ul>
  <li><a href="https://www.techjunkie.com/environment-variables-windows-10/" target="_blank">Open</a> up your environment variables.</li>
  <li>Enter the following environment variables.</li>
</ul>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Corresponding Value</td>
      <td>Value</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">AZURE_CLIENT_ID</code></td>
      <td>The Azure application/client id</td>
      <td><code class="language-plaintext highlighter-rouge">6c04f5c5-97f7-486d-bbb2-eeeeeeeeee</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">AZURE_CLIENT_SECRET</code></td>
      <td>The client secret/password</td>
      <td><code class="language-plaintext highlighter-rouge">QPxyBvw3.UE8Bw6AJAt63pWx~BB40deded</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">AZURE_TENANT_ID</code></td>
      <td>The directory/tenant id</td>
      <td><code class="language-plaintext highlighter-rouge">bee716cf-fa94-4610-b72e-5df4bf5a9999</code></td>
    </tr>
  </tbody>
</table>

<p class="notice--warning"><strong><em>NOTE</em></strong> These are not real values! :smile:</p>

<p class="notice--info"><strong><em>NOTE</em></strong> Depending on your IDE, Terminal, etc, you may need to restart it after updating these values.</p>

<h3 id="mac">Mac</h3>

<p>The procedure may vary depending on your environment/shell. For ZSH/bash, add the following your profile.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nb">export </span><span class="nv">AZURE_CLIENT_ID</span><span class="o">=</span>6c04f5c5-97f7-486d-bbb2-eeeeeeeeee
<span class="nb">export </span><span class="nv">AZURE_CLIENT_SECRET</span><span class="o">=</span>QPxyBvw3.UE8Bw6AJAt63pWx~BB40deded
<span class="nb">export </span><span class="nv">AZURE_TENANT_ID</span><span class="o">=</span>bee716cf-fa94-4610-b72e-5df4bf5a9999
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="next-steps">Next Steps</h2>

<p>Any questions, please feel free to send me and email or tweet.  Like what you see, watch my <a href="https://jjg.me/stream" target="_blank">stream</a> and/or subscribe to my <a href="https://jjg.me/youtube" target="_blank">YouTube channel</a>.</p>

<h3 id="resources">Resources</h3>

<ul>
  <li>Microsoft identity platform <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/?wt.mc_id=DT-MVP-4024623" target="_blank">documentation</a></li>
  <li>Follow the 425show on <a href="https://www.twitch.tv/425show" target="_blank">Twitch</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Identity" /><category term="MSAL" /><category term="Managed Identity" /><category term="Entra" /><summary type="html"><![CDATA[Securing our applications and data is critical in this day and age. I’ve been working a lot with the new Microsoft identity platform (MSAL) library, so I decided to create a series of blog posts around working with it.]]></summary></entry><entry><title type="html">Working with Microsoft Identity - Registering an Application</title><link href="https://www.josephguadagno.net/2020/08/29/working-with-microsoft-identity-registering-an-application" rel="alternate" type="text/html" title="Working with Microsoft Identity - Registering an Application" /><published>2020-08-29T00:00:00-07:00</published><updated>2020-08-29T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/08/29/working-with-microsoft-identity-registering-an-application</id><content type="html" xml:base="https://www.josephguadagno.net/2020/08/29/working-with-microsoft-identity-registering-an-application"><![CDATA[<p>Securing our applications and data is critical in this day and age.  I’ve been working a lot with the new <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/?wt.mc_id=DT-MVP-4024623" target="_blank">Microsoft identity platform</a>  (MSAL) library, so I decided to create a series of blog posts around working with it.</p>

<ul>
  <li>Register an application (this post)</li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-configure-local-development">Configure Local Development</a></li>
  <li><a href="/2020/08/29/working-with-microsoft-identity-assigning-a-role">Assigning a Role</a></li>
</ul>

<p>I’ve been following the pattern of creating a dedicated <em>test</em> application to validate that everything works locally. By <em>Application</em>, I don’t mean an executable or javascript application, I mean <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app?wt.mc_id=DT-MVP-4024623" target="_blank">registering an application with the Microsoft Identity Platform</a>.</p>

<p>There are two ways in which you to do this, the Azure Command Line Interface (CLI) or the Azure Portal.  I’ll demonstrate both.</p>

<h2 id="azure-cli">Azure CLI</h2>

<p>If you don’t have the CLI installed and prefer the command-line, check out the <a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-windows?wt.mc_id=DT-MVP-4024623" target="_blank">installation instructions</a>.</p>

<p>To register your application with Azure using the Azure CLI, open up Terminal, Bash, Command Prompt, ITerm, or whatever your preferred command prompt is.</p>

<p>First, you need to log in with the command line.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">az</span><span class="w"> </span><span class="nx">login</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Once logged in, the next step would be to create the Azure Active Directory service principal.  Creating the service principal <em>registers</em> the application.  You can use the <a href="https://docs.microsoft.com/en-us/cli/azure/ad/sp?view=azure-cli-latest&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">ad sp</a> command, which stands for ‘Active Directory’ ‘Service Principal’. We are going to use <code class="language-plaintext highlighter-rouge">create-for-rbac</code> sub-command. <a href="https://docs.microsoft.com/en-us/cli/azure/ad/sp?view=azure-cli-latest#az-ad-sp-create-for-rbac?wt.mc_id=DT-MVP-4024623" target="_blank">Documentation</a></p>

<p>The command looks similar to this.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="n">az</span><span class="w"> </span><span class="nx">ad</span><span class="w"> </span><span class="nx">sp</span><span class="w"> </span><span class="nx">create-for-rbac</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--name</span><span class="w"> </span><span class="s2">"&lt;name&gt;"</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--role</span><span class="w"> </span><span class="s2">"&lt;role&gt;"</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--scopes</span><span class="w"> </span><span class="nx">/subscriptions/</span><span class="err">&lt;</span><span class="nx">scope-subscription</span><span class="err">&gt;</span><span class="nx">/resourceGroups/</span><span class="err">&lt;</span><span class="nx">scope-resource-group</span><span class="err">&gt;</span><span class="nx">/providers/Microsoft.Storage/storageAccounts/</span><span class="err">&lt;</span><span class="nx">scope-resource-storage</span><span class="err">&gt;</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>NOTE</em></strong> This should be entered in one line, or use the backtick</p>

<p>Replace the following ‘tokens’ with your actual values</p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Description</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">name</code></td>
      <td>The name for the application</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">role</code></td>
      <td>The Azure Active Directory role you want to assign to this application</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">scope-subscription</code></td>
      <td>The subscription id of you Azure subscription</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">scope-resource-group</code></td>
      <td>The resource group that the storage container belongs to</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">scope-resource-storage</code></td>
      <td>The Azure Storage container you want to grant access to</td>
    </tr>
  </tbody>
</table>

<p>Since the <code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">role</code> can contain spaces, you should wrap them in double quotes (“).</p>

<p>Assuming you have the authorization and syntax-correct, the call will return a JSON file that looks like this:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"appId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6c04f5c5-97f7-486d-bbb2-eeeeeeeeee"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"displayName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My Test Application - Local Development"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://MyTestApplication.LocalDevelopment"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"QPxyBvw3.UE8Bw6AJAt63pWx~BB40deded"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"tenant"</span><span class="p">:</span><span class="w"> </span><span class="s2">"bee716cf-fa94-4610-b72e-5df4bf5a9999"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>Note</em></strong> I’ve changed the values here from the values I received.</p>

<p>At this point, you’ll want to save values of the <code class="language-plaintext highlighter-rouge">appId</code>, <code class="language-plaintext highlighter-rouge">password</code>, and <code class="language-plaintext highlighter-rouge">tenant</code>.  Once you close your terminal you will now be able to retrieve the password again.</p>

<p>You can find more about this approach on <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad-rbac-cli?toc=/azure/storage/blobs/toc.json&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Documentation site</a></p>

<h2 id="azure-portal">Azure Portal</h2>

<p>If you are like me, you like to do most of the work in the portal, although I find myself using the command line more.  Let’s take a look registering your application with the portal.</p>

<h3 id="register-the-application">Register the Application</h3>

<p>Start with signing into the portal and navigate to your <a href="https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview" target="_blank">Azure Active Directory</a>. Look for the ‘App Registrations’ blade in the menu on the left and click on it.</p>

<p>Click on ‘+ New registration’</p>

<p><img src="/assets/images/posts/protect-api-create-application.png" alt="Register new Application" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Name</td>
      <td><code class="language-plaintext highlighter-rouge">my application</code></td>
      <td>The name you want to identify with this application</td>
    </tr>
    <tr>
      <td>Supported account types</td>
      <td><code class="language-plaintext highlighter-rouge">Accounts in any organization...</code></td>
      <td>Chose the type that fits your needs</td>
    </tr>
    <tr>
      <td>Redirect URI (Optional)</td>
      <td> </td>
      <td>This is required if you are going to be using the application to sign in.  I’m leaving this blank for this application</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Click ‘Register’</li>
</ul>

<p>You will be presented with the information around the application.  Copy down the ‘Application (client) ID’ and the ‘Directory (tenant) ID’ for use later.</p>

<p><img src="/assets/images/posts/securing-container-app-id.png" alt="New Application" class="align-center" /></p>

<ul>
  <li>Find the ‘Certificates &amp; secrets’ link on the menu on the left and click it.</li>
  <li>Click ‘+ New client secret’</li>
  <li>Add a description.  The Azure CLI names it <code class="language-plaintext highlighter-rouge">rbac</code> but I don’t think the name matters.</li>
  <li>Chose an expires in option.</li>
  <li>Copy the value.</li>
</ul>

<p class="notice--info"><strong><em>NOTE</em></strong> This is your only opportunity to copy it.  If you don’t, you will need to recreate it.</p>

<h2 id="next-steps">Next Steps</h2>

<p>The next step would be to <a href="/2020/08/29/working-with-microsoft-identity-configure-local-development">configure your workstation</a> to use the <em>credentials</em> of the newly registered application.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Identity" /><category term="MSAL" /><category term="Managed Identity" /><category term="Entra" /><summary type="html"><![CDATA[Securing our applications and data is critical in this day and age. I’ve been working a lot with the new Microsoft identity platform (MSAL) library, so I decided to create a series of blog posts around working with it.]]></summary></entry><entry><title type="html">Securing Azure Containers and Blobs with Managed Identities</title><link href="https://www.josephguadagno.net/2020/08/22/securing-azure-containers-and-blobs-with-managed-identities" rel="alternate" type="text/html" title="Securing Azure Containers and Blobs with Managed Identities" /><published>2020-08-22T00:00:00-07:00</published><updated>2020-08-22T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/08/22/securing-azure-containers-and-blobs-with-managed-identities</id><content type="html" xml:base="https://www.josephguadagno.net/2020/08/22/securing-azure-containers-and-blobs-with-managed-identities"><![CDATA[<p>I’ve been streaming ‘Coding with JoeG’ on <a href="https://www.twitch.tv/jguadagno" target="_blank">Twitch</a> for a few months now. The general theme of the stream is teaching software development with C#. We’ve been building a contact management application to demonstrate some <em>best practices</em>.  About two weeks ago, or so, I added an Azure Storage Blob Container to hold the images of the contacts. I did this using the way I knew how to do it using <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?wt.mc_id=DT-MVP-4024623" target="_blank">Storage Access Keys</a> and a library, <a href="https://github.com/jguadagno/JosephGuadagno.AzureHelpers.Storage/" target="_blank">JosephGuadagno.AzureHelpers.Storage</a>, that I wrote to make it ‘<em>easier</em>’ to interact with Azure Storage. While talking about the stream on Twitter, <a href="https://twitter.com/ChristosMatskas" target="_blank">Christos</a>, PM on the Microsoft Identity team, reached out and said I should try securing the Container/Blob with <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad-msi?wt.mc_id=DT-MVP-4024623" target="_blank">Managed Identity</a>.  I tried on the stream for a good 5 or so hours and could not get it to work.  If you want to see it, check out the recording of the stream on my <a href="https://jjg.me/youtube" target="_blank">YouTube</a> channel.</p>

<ul>
  <li>Security Azure Blob Using Microsoft Identity <a href="https://www.youtube.com/watch?v=JX_ysOk-IYM" target="_blank">Part 1</a></li>
  <li>Security Azure Blob Using Microsoft Identity <a href="https://www.youtube.com/watch?v=xwoMnUZVafo" target="_blank">Part 2</a></li>
  <li>Security Azure Blob Using Microsoft Identity <a href="https://www.youtube.com/watch?v=Z4kaYQKU-Us" target="_blank">Part 3</a></li>
  <li>Security Azure Blob Using Microsoft Identity <a href="https://www.youtube.com/watch?v=kksVNpRX2jU" target="_blank">Part 4</a></li>
  <li>Security Azure Blob Using Microsoft Identity <a href="https://www.youtube.com/watch?v=mc9ODk121og" target="_blank">Part 5</a></li>
</ul>

<p>Well, I was playing around yesterday after reading the <a href="https://azure.github.io/azure-sdk/" target="_blank">Azure SDK</a> - <a href="https://devblogs.microsoft.com/azure-sdk/azure-identity-august-2020-ga/?WT.mc_id=DT-MVP-4024623" target="_blank">Azure Identity August 2020 General Availability Release</a> notes, wow that is a long title! :smile:, and I got it to work! :relieved:</p>

<p>The basic steps are:</p>

<ol>
  <li>Register an application with Azure</li>
  <li>Assign a role to that application. More on this later.</li>
  <li>Allow that application access to the container.</li>
  <li>Pass the identity of the application to the Azure SDK or <a href="https://github.com/jguadagno/JosephGuadagno.AzureHelpers.Storage/" target="_blank">JosephGuadagno.AzureHelpers.Storage</a>.</li>
  <li>Celebrate your secure connection!</li>
</ol>

<p>But before we talk about securing Azure Storage Blob Containers, let’s look out what <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/overview?wt.mc_id=DT-MVP-4024623" target="_blank">Azure RBAC</a> is. Azure RBAC, or Azure Role-Based Access Control, is an authorization system built on Azure Resource Manager that provides fine-grained access management of Azure resources.  It allows you to create roles or use predefined roles for your applications.  Azure Storages has many predefined <a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage?wt.mc_id=DT-MVP-4024623" target="_blank">roles</a>. The Azure Storage Container ones are as follows:</p>

<table>
  <tbody>
    <tr>
      <td>Role</td>
      <td>Description</td>
    </tr>
    <tr>
      <td><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage-blob-data-owner?wt.mc_id=DT-MVP-4024623" target="_blank">Storage Blob Data Owner</a></td>
      <td>Use to set ownership and manage POSIX access control for Azure Data Lake Storage Gen2. For more information, see Access control in Azure Data Lake Storage Gen2.</td>
    </tr>
    <tr>
      <td><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage-blob-data-contributor?wt.mc_id=DT-MVP-4024623" target="_blank">Storage Blob Data Contributor</a></td>
      <td>Use to grant read/write/delete permissions to Blob storage resources.</td>
    </tr>
    <tr>
      <td><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage-blob-data-reader?wt.mc_id=DT-MVP-4024623" target="_blank">Storage Blob Data Reader</a></td>
      <td>Use to grant read-only permissions to Blob storage resources.</td>
    </tr>
    <tr>
      <td><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage-blob-delegator" target="_blank">Storage Blob Delegator</a></td>
      <td>Get a user delegation key to use to create a shared access signature that is signed with Azure AD credentials for a container or blob.</td>
    </tr>
    <tr>
      <td><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage-queue-data-contributor" target="_blank">Storage Queue Data Contributor</a></td>
      <td>Use to grant read/write/delete permissions to Azure queues.</td>
    </tr>
    <tr>
      <td><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage-queue-data-reader?wt.mc_id=DT-MVP-4024623" target="_blank">Storage Queue Data Reader</a></td>
      <td>Use to grant read-only permissions to Azure queues.</td>
    </tr>
    <tr>
      <td><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage-queue-data-message-processor?wt.mc_id=DT-MVP-4024623" target="_blank">Storage Queue Data Message Processor</a></td>
      <td>Use to grant peek, retrieve, and delete permissions to messages in Azure Storage queues.</td>
    </tr>
    <tr>
      <td><a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#storage-queue-data-message-sender?wt.mc_id=DT-MVP-4024623" target="_blank">Storage Queue Data Message Sender</a></td>
      <td>Use to grant add permissions to messages in Azure Storage queues.</td>
    </tr>
  </tbody>
</table>

<h2 id="registering-the-application">Registering the Application</h2>

<p>The first step in getting authentication working for your application is registering the application with Azure.  There are two different ways to register your application.  I’ll show you both ways here.</p>

<h3 id="azure-cli">Azure CLI</h3>

<p>If you don’t have the CLI installed and you prefer the command, check out the <a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli-windows?wt.mc_id=DT-MVP-4024623" target="_blank">installation instructions</a>.</p>

<p>To register your application with Azure using the Azure CLI, open up Terminal, Bash, Command Prompt, ITerm, or whatever your preferred command prompt is.</p>

<p>First, you need to log in with the command line.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">az</span><span class="w"> </span><span class="nx">login</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>After you are logged in, the next step would be to create the Azure Active Directory service principal.  This <em>registers</em> the application.  You can use the <a href="https://docs.microsoft.com/en-us/cli/azure/ad/sp?view=azure-cli-latest?wt.mc_id=DT-MVP-4024623" target="_blank">ad sp</a> command, which stands for ‘Active Directory’ ‘Service Principal’. We are going to use <code class="language-plaintext highlighter-rouge">create-for-rbac</code> sub-command <a href="https://docs.microsoft.com/en-us/cli/azure/ad/sp?view=azure-cli-latest#az-ad-sp-create-for-rbac?wt.mc_id=DT-MVP-4024623" target="_blank">Documentation</a>.</p>

<p>The command looks similar to this.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="n">az</span><span class="w"> </span><span class="nx">ad</span><span class="w"> </span><span class="nx">sp</span><span class="w"> </span><span class="nx">create-for-rbac</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--name</span><span class="w"> </span><span class="s2">"&lt;name&gt;"</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--role</span><span class="w"> </span><span class="s2">"&lt;role&gt;"</span><span class="w"> </span><span class="se">`
</span><span class="w">  </span><span class="nt">--scopes</span><span class="w"> </span><span class="nx">/subscriptions/</span><span class="err">&lt;</span><span class="nx">scope-subscription</span><span class="err">&gt;</span><span class="nx">/resourceGroups/</span><span class="err">&lt;</span><span class="nx">scope-resource-group</span><span class="err">&gt;</span><span class="nx">/providers/Microsoft.Storage/storageAccounts/</span><span class="err">&lt;</span><span class="nx">scope-resource-storage</span><span class="err">&gt;</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>NOTE</em></strong> This should be entered in one line, or use the backtick</p>

<p>Replace the following ‘tokens’ with your actual values</p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Description</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">name</code></td>
      <td>The name for the application</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">role</code></td>
      <td>The Azure Active Directory role you want to assign to this application</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">scope-subscription</code></td>
      <td>The subscription id of you Azure subscription</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">scope-resource-group</code></td>
      <td>The resource group that the storage container belongs to</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">scope-resource-storage</code></td>
      <td>The Azure Storage container you want to grant access to</td>
    </tr>
  </tbody>
</table>

<p>Since the <code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">role</code> can contain spaces, you should wrap them in double quotes (“).</p>

<p>Assuming you have the authorization and syntax-correct, the call will return a JSON file that looks like this:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="p">{</span><span class="w">
  </span><span class="nl">"appId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6c04f5c5-97f7-486d-bbb2-eeeeeeeeee"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"displayName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"HelpersTestRole"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://HelpersTestRole"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"password"</span><span class="p">:</span><span class="w"> </span><span class="s2">"QPxyBvw3.UE8Bw6AJAt63pWx~BB40deded"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"tenant"</span><span class="p">:</span><span class="w"> </span><span class="s2">"bee716cf-fa94-4610-b72e-5df4bf5a9999"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--info"><strong><em>Note</em></strong> I’ve changed the values here from the values I received.</p>

<p>At this point, you’ll want to save values of the <code class="language-plaintext highlighter-rouge">appId</code>, <code class="language-plaintext highlighter-rouge">password</code>, and <code class="language-plaintext highlighter-rouge">tenant</code>.  Once you close your terminal you will now be able to retrieve the password again.</p>

<p>You can find more about this approach on <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad-rbac-cli?toc=/azure/storage/blobs/toc.json&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Documentation site</a></p>

<h3 id="azure-portal">Azure Portal</h3>

<p>If you are like me, you like to do most of the work in the portal, although I find myself using the command line more.  Let’s take a look at registering your application with the portal.</p>

<h4 id="register-the-application">Register the Application</h4>

<p>Start off with signing into the portal and navigate to your <a href="https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview" target="_blank">Azure Active Directory</a>. Look for the ‘App Registrations’ blade in the menu on the left and click on it.</p>

<p>Click on ‘+ New registration’</p>

<p><img src="/assets/images/posts/protect-api-create-application.png" alt="Register new Application" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Name</td>
      <td><code class="language-plaintext highlighter-rouge">my application</code></td>
      <td>The name you want to identify with this application</td>
    </tr>
    <tr>
      <td>Supported account types</td>
      <td><code class="language-plaintext highlighter-rouge">Accounts in any organization...</code></td>
      <td>Chose the type that fits your needs</td>
    </tr>
    <tr>
      <td>Redirect URI (Optional)</td>
      <td> </td>
      <td>This is required if you are going to be using the application to sign in.  I’m leaving this blank for local development</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Click ‘Register’</li>
</ul>

<p>You will be presented with the information around the application.  Copy down the ‘Application (client) ID’ and the ‘Directory (tenant) ID’ for use later.</p>

<p><img src="/assets/images/posts/securing-container-app-id.png" alt="New Application" class="align-center" /></p>

<ul>
  <li>Find the ‘Certificates &amp; secrets’ link on the menu on the left and click it.</li>
  <li>Click ‘+ New client secret’</li>
  <li>Add a description.  The Azure CLI names it <code class="language-plaintext highlighter-rouge">rbac</code> but I don’t think the name matters.</li>
  <li>Chose an expires in option.</li>
  <li>Copy the value.</li>
</ul>

<p class="notice--info"><strong><em>NOTE</em></strong> This is your only opportunity to copy it.  If you don’t, you will need to recreate it.</p>

<h4 id="assign-the-role">Assign the Role</h4>

<p>Now we need to assign a role to the application which allows the application to use the Azure Storage container.</p>

<p>Navigate to the container that you want to provide access and click on ‘Access control (IAM’) on the left menu.</p>

<p>There are two ways to add the role.  Option 1, click the ‘+ Add’, then ‘Add role assignment’. Option 2, click the ‘Add’ button.</p>

<p><img src="/assets/images/posts/securing-container-add-role.png" alt="Add IAM Role" class="align-center" /></p>

<p>Enter the following:</p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Role</td>
      <td><code class="language-plaintext highlighter-rouge">Storage Blob Data Contributor</code></td>
      <td>Add what makes sense for your application.  Not sure what setting to use, hover over the ‘i’ or check out the permissions mentioned earlier in this post.</td>
    </tr>
    <tr>
      <td>Assign access to</td>
      <td><code class="language-plaintext highlighter-rouge">Azure AD user, group, or service principal</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Select</td>
      <td><em>The name you entered in the previous step</em></td>
      <td>The default will be your user id.  Type in the first couple of characters of the application you added in the previous step</td>
    </tr>
  </tbody>
</table>

<p><img src="/assets/images/posts/securing-container-add-role-assignment.png" alt="Add Role Assignment" class="align-center" /></p>

<ul>
  <li>Click ‘Save’</li>
</ul>

<p>You can verify the changes by using the ‘Check access’ section of the blade.</p>

<p>You can find more about this approach on <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-auth-aad-rbac-portal?toc=/azure/storage/blobs/toc.json&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Documentation site</a></p>

<h2 id="updating-the-application-code">Updating the Application Code</h2>

<p>The <a href="https://azure.github.io/azure-sdk/" target="_blank">Azure SDK</a> added support for using credentials instead of just the shared storage keys. I added the same support to <a href="https://github.com/jguadagno/JosephGuadagno.AzureHelpers.Storage/" target="_blank">JosephGuadagno.AzureHelpers.Storage</a>, since underneath the hood it uses the Azure SDK.</p>

<p>Both SDKs take a <a href="https://docs.microsoft.com/en-us/dotnet/api/azure.core.tokencredential?view=azure-dotnet&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">TokenCredential</a> that can be one of many types.  For this post, we are going to allow the SDK to do the work for us by providing the <code class="language-plaintext highlighter-rouge">DefaultAzureCredential</code>.  The <code class="language-plaintext highlighter-rouge">DefaultAzureCredential</code> will look through many ‘<em>different sources</em>’ for credential data, including but not limited to, the <strong>Environment</strong>, <strong>Shared Token Cache</strong>, <strong>Visual Studio</strong>, <strong>IntelliJ</strong>, and many more. You read more about it in the <a href="https://devblogs.microsoft.com/azure-sdk/azure-identity-august-2020-ga/?WT.mc_id=DT-MVP-4024623" target="_blank">Azure Identity August 2020 GA Release Notes</a>.</p>

<h3 id="using-the-azure-sdk">Using the Azure SDK</h3>

<p>As of the writing of this post, you would need version <code class="language-plaintext highlighter-rouge">1.2.0</code> of the Azure SDK</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Install-Package</span><span class="w"> </span><span class="nx">Azure.Identity</span><span class="w"> </span><span class="nt">-Version</span><span class="w"> </span><span class="nx">1.2.0</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Replace <code class="language-plaintext highlighter-rouge">&lt;storage-account&gt;</code> with the name of your Azure Storage account</p>

<p>To connect to a blob client, you would use the following code:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="c1">// Create a BlobServiceClient that will authenticate through Active Directory</span>
<span class="n">Uri</span> <span class="n">accountUri</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"https://&lt;storage-account&gt;.blob.core.windows.net/"</span><span class="p">);</span>
<span class="n">BlobServiceClient</span> <span class="n">client</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">BlobServiceClient</span><span class="p">(</span><span class="n">accountUri</span><span class="p">,</span> <span class="k">new</span> <span class="nf">DefaultAzureCredential</span><span class="p">());</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>To create a blob</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="k">private</span> <span class="k">static</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="kt">string</span><span class="p">&gt;</span> <span class="nf">CreateBlob</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Replace the URL below with your storage account URL</span>
    <span class="n">CloudBlockBlob</span> <span class="n">blob</span> <span class="p">=</span>
        <span class="k">new</span> <span class="nf">CloudBlockBlob</span><span class="p">(</span>
            <span class="k">new</span> <span class="nf">Uri</span><span class="p">(</span><span class="s">"https://&lt;storage-account&gt;.blob.core.windows.net/&lt;container&gt;/Blob1.txt"</span><span class="p">),</span>
            <span class="k">new</span> <span class="nf">DefaultAzureCredentials</span><span class="p">());</span>
    <span class="k">await</span> <span class="n">blob</span><span class="p">.</span><span class="nf">UploadTextAsync</span><span class="p">(</span><span class="s">"Blob created by Azure AD authenticated user."</span><span class="p">);</span>
    <span class="k">return</span> <span class="s">"Blob successfully created"</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="using-josephguadagnoazurehelpersstorage">Using JosephGuadagno.AzureHelpers.Storage</h3>

<p>As of the writing of this post, you would need version <code class="language-plaintext highlighter-rouge">1.1.0-20200821.1-preview</code> of the library. It’s in preview because I have to finish the docs and tests for the newly added methods.</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">Install-Package</span><span class="w"> </span><span class="nx">JosephGuadagno.AzureHelpers.Storage</span><span class="w"> </span><span class="nt">-Version</span><span class="w"> </span><span class="nx">1.1.0-20200821.1-preview</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Replace <code class="language-plaintext highlighter-rouge">storageAccount</code> with the name of your Azure Storage account.</p>

<p>Replace <code class="language-plaintext highlighter-rouge">containerName</code> with the name of the Azure Storage container.</p>

<p>To connect to a blob client, you would use the following code:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">blobs</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Blobs</span><span class="p">(</span><span class="n">storageAccount</span><span class="p">,</span> <span class="k">null</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>To create a blob</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">blobs</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">Blobs</span><span class="p">(</span><span class="n">storageAccount</span><span class="p">,</span> <span class="k">null</span><span class="p">,</span> <span class="n">containerName</span><span class="p">);</span>
<span class="k">await</span> <span class="n">blobs</span><span class="p">.</span><span class="nf">UploadAsync</span><span class="p">(</span><span class="s">"blob1.txt"</span><span class="p">,</span> <span class="s">"path_to_file"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="local-development">Local Development</h3>

<p>Your setup may vary depending on the IDE you are using, Visual Studio, Jetbrains Rider, IntelliJ, Visual Studio Code, etc.  I’m going to show you how to set up your <em>Environment</em> variables to use the <code class="language-plaintext highlighter-rouge">DefaultAzureCredentials</code>.  For this, you will need the Application (Client) ID, Directory (Tenant) ID, and Client Secret (password).</p>

<h4 id="windows">Windows</h4>

<ul>
  <li><a href="https://www.techjunkie.com/environment-variables-windows-10/" target="_blank">Open</a> up your environment variables.</li>
  <li>Enter the following environment variables.</li>
</ul>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Corresponding Value</td>
      <td>Value</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">AZURE_CLIENT_ID</code></td>
      <td>The Azure application/client id</td>
      <td><code class="language-plaintext highlighter-rouge">6c04f5c5-97f7-486d-bbb2-eeeeeeeeee</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">AZURE_CLIENT_SECRET</code></td>
      <td>The client secret/password</td>
      <td><code class="language-plaintext highlighter-rouge">QPxyBvw3.UE8Bw6AJAt63pWx~BB40deded</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">AZURE_TENANT_ID</code></td>
      <td>The directory/tenant id</td>
      <td><code class="language-plaintext highlighter-rouge">bee716cf-fa94-4610-b72e-5df4bf5a9999</code></td>
    </tr>
  </tbody>
</table>

<p class="notice--warning"><strong><em>NOTE</em></strong> These are not real values! :smile: These values were taken from the sample JSON response above.</p>

<p class="notice--info"><strong><em>NOTE</em></strong> Depending on your IDE, Terminal, etc, you may need to restart it after updating these values.</p>

<h4 id="mac">Mac</h4>

<p>The procedure may vary depending on your environment/shell. For ZSH/bash, add the following your profile.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nb">export </span><span class="nv">AZURE_CLIENT_ID</span><span class="o">=</span>6c04f5c5-97f7-486d-bbb2-eeeeeeeeee
<span class="nb">export </span><span class="nv">AZURE_CLIENT_SECRET</span><span class="o">=</span>QPxyBvw3.UE8Bw6AJAt63pWx~BB40deded
<span class="nb">export </span><span class="nv">AZURE_TENANT_ID</span><span class="o">=</span>bee716cf-fa94-4610-b72e-5df4bf5a9999
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="azure-app-service">Azure App Service</h3>

<p>To add the access permissions to an Azure Storage Account to an App Service is identical to adding it to our app that we did <a href="#assign-the-role">previously</a>. The only different is we select the name of the app service we want to provide the access to.</p>

<h2 id="next-steps">Next Steps</h2>

<p>Wow, that was a lot.  Any questions, please feel free to send me and email or tweet.  Like what you see, watch my <a href="https://jjg.me/stream" target="_blank">stream</a> and/or subscribe to my <a href="https://jjg.me/youtube" target="_blank">YouTube channel</a>.</p>

<h3 id="resources">Resources</h3>

<ul>
  <li>Microsoft identity platform <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/?wt.mc_id=DT-MVP-4024623" target="_blank">documentation</a></li>
  <li>Follow the 425show on <a href="https://www.twitch.tv/425show" target="_blank">Twitch</a></li>
</ul>

<p>A recording of me explaining this on my <a href="https://jjg.me/stream" target="_blank">Twitch</a> stream.</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/cR_ZGt363p4" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Containers" /><category term="Blob" /><category term="Identity" /><category term="MSAL" /><category term="Managed Identity" /><summary type="html"><![CDATA[I’ve been streaming ‘Coding with JoeG’ on Twitch for a few months now. The general theme of the stream is teaching software development with C#. We’ve been building a contact management application to demonstrate some best practices. About two weeks ago, or so, I added an Azure Storage Blob Container to hold the images of the contacts. I did this using the way I knew how to do it using Storage Access Keys and a library, JosephGuadagno.AzureHelpers.Storage, that I wrote to make it ‘easier’ to interact with Azure Storage. While talking about the stream on Twitter, Christos, PM on the Microsoft Identity team, reached out and said I should try securing the Container/Blob with Managed Identity. I tried on the stream for a good 5 or so hours and could not get it to work. If you want to see it, check out the recording of the stream on my YouTube channel.]]></summary></entry><entry><title type="html">Debugging Azure Function Event Grid Triggers Locally with JetBrains Rider</title><link href="https://www.josephguadagno.net/2020/07/20/debugging-azure-function-event-grid-trigger-locally-with-jetbrains-rider" rel="alternate" type="text/html" title="Debugging Azure Function Event Grid Triggers Locally with JetBrains Rider" /><published>2020-07-20T00:00:00-07:00</published><updated>2020-07-20T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/07/20/debugging-azure-function-event-grid-trigger-locally-with-jetbrains-rider</id><content type="html" xml:base="https://www.josephguadagno.net/2020/07/20/debugging-azure-function-event-grid-trigger-locally-with-jetbrains-rider"><![CDATA[<p>If you’ve done much with <a href="https://docs.microsoft.com/en-us/azure/azure-functions/?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Functions</a> and the <a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-event-grid-trigger?wt.mc_id=DT-MVP-4024623" target="_blank">Event Grid triggers</a>, you’ve probably come across the post ‘<a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-debug-event-grid-trigger-local?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Function Event Grid Trigger Local Debugging</a>’. Even searching for ‘Azure Function Event Grid Trigger’ on <a href="https://www.bing.com/search?q=azure+function+event+grid+trigger" target="_blank">Bing</a> and it’s the first result that comes up, at least for me. :smile: The post itself, at the time of me writing this, is almost two years old. While most of the post is still valid, some of Azure has changed, and the post is based on using Visual Studio. So this post is meant to be a modernized version of that post using JetBrains Rider.</p>

<h2 id="before-you-get-started">Before you get started</h2>

<p>Here are a few things that you are going to need for this to work.</p>

<ul>
  <li>You’ll need an existing function application.</li>
  <li>You’ll need an Azure subscription to create the EventGrid trigger</li>
  <li>You’ll need to create an <a href="https://dashboard.ngrok.com/signup" target="_blank">account</a> on <a href="https://ngrok.com/" target="_blank">ngrok</a> and download <a href="https://ngrok.com/download" target="_blank">ngrok</a>.</li>
  <li>If you are using JetBrains Rider, you will need the ‘Azure Toolkit for Rider’ plugin. You can read this <a href="https://khalidabuhakmeh.com/azure-functions-on-macos-with-jetbrains-rider" target="_blank">post</a> to get started with Azure Functions and JetBrains Rider</li>
</ul>

<h2 id="running-ngrok">Running ngrok</h2>

<p>Ngrok provides a ‘pass-though’ or webhook from the Azure ecosystem to your local machine. Assuming you have installed <code class="language-plaintext highlighter-rouge">ngrok</code>, open up a terminal, command prompt, or Powershell and navigate to the directory that <code class="language-plaintext highlighter-rouge">ngrok</code> is installed in and execute the following command</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>.<span class="se">\n</span>grok http <span class="nt">-host-header</span><span class="o">=</span>localhost 7071
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>Note</strong>, the <code class="language-plaintext highlighter-rouge">.\</code> is required for terminal and Powershell.  The port number, <code class="language-plaintext highlighter-rouge">7071</code> in this example, is the port that you are ‘debugging’ your Azure function on.  <code class="language-plaintext highlighter-rouge">7071</code> is the default port number.  If you change it or run multiple functions, you are going to want to change the above command.</p>

<p>This will start ngrok with a screen similar to this.</p>

<p><img src="/assets/images/posts/debug-azure-functions-locally-ngrok.png" alt="ngrok" class="align-center" /></p>

<p>Once started, we are ready to head over to Azure and create the ‘hooks’.</p>

<h2 id="event-grid-subscription">Event Grid Subscription</h2>

<p>This post assumes that you have the Event Grid topic setup already, if not, you can read about topics <a href="https://docs.microsoft.com/en-us/azure/event-grid/concepts#topics?wt.mc_id=DT-MVP-4024623" target="_blank">here</a>.</p>

<p>In the Azure portal, navigate to your topic.  In this example we will use the topic of <code class="language-plaintext highlighter-rouge">new-source-data</code>.  It should look similar to this.</p>

<p><img src="/assets/images/posts/debug-azure-functions-locally-topic.png" alt="Event Topic" class="align-center" /></p>

<ul>
  <li>Click on <code class="language-plaintext highlighter-rouge">+ Event Subscription</code></li>
</ul>

<p>That will take you to the ‘Create Event Subscription’ blade.</p>

<p><img src="/assets/images/posts/debug-azure-functions-locally-create-event-subscription.png" alt="Create Event Subscription" class="align-center" /></p>

<p>Enter the following</p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Name</td>
      <td><code class="language-plaintext highlighter-rouge">function_name_webhook</code></td>
      <td>The name can be whatever you want, I base the name off of the function, so in this example <code class="language-plaintext highlighter-rouge">new_source_data_webhook</code></td>
    </tr>
    <tr>
      <td>Event Schema</td>
      <td><code class="language-plaintext highlighter-rouge">Event Grid Schema</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>End Point Type</td>
      <td>Select <code class="language-plaintext highlighter-rouge">Web Hook</code></td>
      <td>This will enable the ‘Select an endpoint’ link</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Clicking on the <code class="language-plaintext highlighter-rouge">Select an endpoint</code> link will bring up the <code class="language-plaintext highlighter-rouge">Select Web Hook</code> dialog.</li>
  <li>Enter the name of the endpoint for the <code class="language-plaintext highlighter-rouge">Subscriber Endpoint</code>. See the next section for the details behind the <code class="language-plaintext highlighter-rouge">Subscriber Endpoint</code> name.</li>
</ul>

<h3 id="subscriber-endpoint">Subscriber Endpoint</h3>

<p>This is where NGrok and Rider come in and the ordering of events matter.</p>

<p>For this example, the full endpoint name would be <code class="language-plaintext highlighter-rouge">https://9ccb49e057a0.ngrok.io/runtime/webhooks/EventGrid?functionName=twitter_process_new_source_data</code>.</p>

<p>The endpoint name is made up of three parts. The first, the url from ngrok which is <code class="language-plaintext highlighter-rouge">https://9ccb49e057a0.ngrok.io</code> in the sample image above under <em>forwarding</em>. The second is the text <code class="language-plaintext highlighter-rouge">/runtime/webhooks/EventGrid?functionName=</code>. The last part is the name of the Azure function, <code class="language-plaintext highlighter-rouge">twitter_process_new_source_data</code> in this example.</p>

<p>Parts two and three can be retrieved from the Rider console once the function is started in Debug.</p>

<p><img src="/assets/images/posts/debug-azure-functions-locally-rider-log.png" alt="Rider Log" class="align-center" /></p>

<p>After the debugging session is started, you can enter the endpoint name in the <code class="language-plaintext highlighter-rouge">Subscriber Endpoint</code> field.</p>

<ul>
  <li>Click <code class="language-plaintext highlighter-rouge">Confirm Selection</code></li>
  <li>Click <code class="language-plaintext highlighter-rouge">Create</code></li>
</ul>

<p>Once you click <code class="language-plaintext highlighter-rouge">Create</code>, Azure sends a test message to the endpoint. If everything was started and entered correctly you will see a message in ngrok console <strong>POST /runtime/webhooks.EventGrid 200 OK</strong>.</p>

<p>If something else is wrong, you may receive one of these two exceptions:</p>

<p><strong>POST <em>misconfigured url</em> 502 Bad Gateway</strong>, this means that you entered something wrong with the <code class="language-plaintext highlighter-rouge">Subscriber Endpoint</code>.</p>

<p><strong>POST /runtime/webhooks/EventGrid 503 Service Unavailable</strong>, this means you probably forgot to start debugging the function.</p>

<h2 id="wrap-up">Wrap up</h2>

<p>Assuming you have the configuration correct you are good to debug your Event Grid triggered function.</p>

<p>A couple of things to note.</p>

<ol>
  <li>Once you close ngrok, you will need to delete the old subscription and create a new one since ngrok generates a new Url.</li>
  <li>The order in which you start things matters.
    <ol>
      <li>In Azure, create the new Event Subscription</li>
      <li>Start ngrok locally</li>
      <li>Start debugging the Azure Function locally. This ensures that ngrok has somewhere to send the ‘hook’.</li>
      <li>Enter the <code class="language-plaintext highlighter-rouge">Subscriber Endpoint</code> in Azure</li>
      <li><code class="language-plaintext highlighter-rouge">Create</code> the subscription</li>
    </ol>
  </li>
</ol>

<p>If you followed the order, ngrok should get a test message and then you are ready to debug so start sending messages to the topic.</p>

<p>Enjoy!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Functions" /><category term="Rider" /><category term="Event Grid" /><summary type="html"><![CDATA[If you’ve done much with Azure Functions and the Event Grid triggers, you’ve probably come across the post ‘Azure Function Event Grid Trigger Local Debugging’. Even searching for ‘Azure Function Event Grid Trigger’ on Bing and it’s the first result that comes up, at least for me. :smile: The post itself, at the time of me writing this, is almost two years old. While most of the post is still valid, some of Azure has changed, and the post is based on using Visual Studio. So this post is meant to be a modernized version of that post using JetBrains Rider.]]></summary></entry><entry><title type="html">Securing Azure Function Settings with Azure Key Vault</title><link href="https://www.josephguadagno.net/2020/07/10/securing-azure-function-settings-with-azure-key-vault" rel="alternate" type="text/html" title="Securing Azure Function Settings with Azure Key Vault" /><published>2020-07-10T00:00:00-07:00</published><updated>2020-07-10T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/07/10/securing-azure-function-settings-with-azure-key-vault</id><content type="html" xml:base="https://www.josephguadagno.net/2020/07/10/securing-azure-function-settings-with-azure-key-vault"><![CDATA[<p>In this post, we’ll walk through how you can use <a href="https://docs.microsoft.com/en-us/azure/key-vault/key-vault-overview?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault</a> to secure sensitive settings in <a href="https://azure.microsoft.com/en-us/services/functions/?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Functions</a>. If you don’t have a Key Vault setup, I covered setting one up in the post titled ‘<a href="/2020/04/04/setup-code-signing-certificates-in-azure-key-vault">Setup Code Signing Certificates in Azure Key Value</a>’</p>

<h2 id="azure-key-vault">Azure Key Vault</h2>

<h3 id="setting-up-a-secret">Setting up a Secret</h3>

<p>If you have secrets set up in your Key Vault you jump to <a href="#secret-details">Secret Details</a>. If not, let’s create a secret in your Key Vault.  In the Azure Portal, got to your Key Vault and click on <code class="language-plaintext highlighter-rouge">Secrets</code> in the Settings section on the left.</p>

<p><img src="/assets/images/posts/secure-azure-function-key-vault-settings.png" alt="Azure Key Vault Secrets" class="align-center" /></p>

<ul>
  <li>Click on <code class="language-plaintext highlighter-rouge">+ Generate/Import</code> and you will come to the ‘Create a secret’ blade.</li>
</ul>

<p><img src="/assets/images/posts/secure-azure-function-key-vault-create-secret.png" alt="Azure Key Vault - Create a Secret" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Upload Options</td>
      <td><code class="language-plaintext highlighter-rouge">Manual</code></td>
      <td>Choose manual unless you want to upload a certificate</td>
    </tr>
    <tr>
      <td>Name</td>
      <td><code class="language-plaintext highlighter-rouge">the-name-of-the-secret</code></td>
      <td>Enter the name for the secret, this should be easy to know what the secret is for. Example: <code class="language-plaintext highlighter-rouge">db-connection</code>, <code class="language-plaintext highlighter-rouge">twitter-client</code>, etc.</td>
    </tr>
    <tr>
      <td>Value</td>
      <td><code class="language-plaintext highlighter-rouge">the-secret</code></td>
      <td>Enter (paste) the secret value</td>
    </tr>
    <tr>
      <td>Content Type</td>
      <td> </td>
      <td>Put in an optional content type. This helps when serving the secrets</td>
    </tr>
    <tr>
      <td>Set activation date?</td>
      <td> </td>
      <td>Unchecked to have it be active when you click <code class="language-plaintext highlighter-rouge">Create</code></td>
    </tr>
    <tr>
      <td>Set expiration date?</td>
      <td> </td>
      <td>Unchecked to have the secret never expire</td>
    </tr>
    <tr>
      <td>Enabled?</td>
      <td><code class="language-plaintext highlighter-rouge">Yes</code></td>
      <td>Whether or not to enable the secret on create</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Click <code class="language-plaintext highlighter-rouge">Create</code></li>
</ul>

<h3 id="secret-details">Secret Details</h3>

<p>Now that you have created the secret or already have one, we need to go to secret details to get the Url for the secret.</p>

<ul>
  <li>Click on the secret name in your Key Vault secrets.</li>
</ul>

<p>This will take you to blade that looks like this.</p>

<p><img src="/assets/images/posts/secure-azure-function-key-vault-secret-info.png" alt="Azure Key Vault - Secret Info" class="align-center" /></p>

<ul>
  <li>Click on the current version number. <code class="language-plaintext highlighter-rouge">971663857bc3477ab80f0de1335dad65</code> in this screen shot.</li>
</ul>

<p>That takes you to the secret details</p>

<p><img src="/assets/images/posts/secure-azure-function-key-vault-secret-details.png" alt="Azure Key Vault - Secret Details" class="align-center" /></p>

<p>You’ll notice, as in this screenshot, there is a copy button highlighted next to the <strong>Secret Identifier</strong> that says <code class="language-plaintext highlighter-rouge">Copy to Clipboard</code>.  Click that button and paste that Url somewhere.  We’ll need it in a future step.  In this example, the value is <code class="language-plaintext highlighter-rouge">https://personalsecrets.vault.azure.net/secrets/the-name-of-the-secret/971663857bc3477ab80f0de1335dad65</code>.  If you notice, the format of the url is <code class="language-plaintext highlighter-rouge">&lt;vault_name&gt;.vault.azure.net/secrets/&lt;name-of-secret&gt;/&lt;secret-version-number&gt;</code>.</p>

<h3 id="granting-access-to-the-secret">Granting Access to the Secret</h3>

<p>Assuming you have the Azure Function App set up already, click on <code class="language-plaintext highlighter-rouge">Access control (IAM)</code> in the left menu. Note, you may have to go back to the home of your Key Vault to see it.</p>

<ul>
  <li>Click on <code class="language-plaintext highlighter-rouge">+ Add</code>, then <code class="language-plaintext highlighter-rouge">Role Assignment</code></li>
</ul>

<p><img src="/assets/images/posts/secure-azure-function-key-vault-add-role-assignment.png" alt="Azure Key Vault - Add Role Assignment" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Role</td>
      <td><code class="language-plaintext highlighter-rouge">Reader</code></td>
      <td>We only need <em>read</em> permissions for this</td>
    </tr>
    <tr>
      <td>Assign access to</td>
      <td><code class="language-plaintext highlighter-rouge">Azure AD user, group, or service principal</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Select</td>
      <td><code class="language-plaintext highlighter-rouge">*your function name*</code></td>
      <td>As you start typing the name, it should start searching for resources that match that</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Select the match</li>
  <li>Then click save.</li>
</ul>

<h2 id="azure-function">Azure Function</h2>

<p>Now head over to your Azure Function. In the <code class="language-plaintext highlighter-rouge">Settings</code> group, click on <code class="language-plaintext highlighter-rouge">Configuration</code>. If you need to create the setting click on <code class="language-plaintext highlighter-rouge">+ New application setting</code>, otherwise click on an existing setting.</p>

<p><img src="/assets/images/posts/secure-azure-function-function-setting.png" alt="Azure Key Vault - Add/Edit application settings" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Name</td>
      <td><code class="language-plaintext highlighter-rouge">the-name-of-the-secret</code></td>
      <td>The name you want to call the setting</td>
    </tr>
    <tr>
      <td>Value</td>
      <td><code class="language-plaintext highlighter-rouge">@Microsoft.KeyVault(SecretUri=https://personalsecrets.vault.azure.net/secrets/the-name-of-the-secret/971663857bc3477ab80f0de1335dad65)</code></td>
      <td>This is the vault you copied previously</td>
    </tr>
  </tbody>
</table>

<p>The format of the value is <code class="language-plaintext highlighter-rouge">@Microsoft.KeyVault(SecretUri=&lt;secret-url&gt;)</code>. Replace the <code class="language-plaintext highlighter-rouge">&lt;secret-url&gt;</code> which whatever was copied from the Key Vault Secrets.</p>

<p>Click <code class="language-plaintext highlighter-rouge">Ok</code> to save the secret. Then click <code class="language-plaintext highlighter-rouge">Save</code> to save the setting(s) to your function.  Once the vault is validated, usually in a few seconds, you will see the value in the <strong>Source</strong> column for that setting change to <code class="language-plaintext highlighter-rouge">Key vault Reference</code>.</p>

<h2 id="wrap-up">Wrap up</h2>

<p>That’s it. Your application can now only be viewed by someone with the correct permissions in the Key Vault. However, a developer can see the secret in the code if they log it, I think.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Key Vault" /><category term="Functions" /><summary type="html"><![CDATA[In this post, we’ll walk through how you can use Azure Key Vault to secure sensitive settings in Azure Functions. If you don’t have a Key Vault setup, I covered setting one up in the post titled ‘Setup Code Signing Certificates in Azure Key Value’]]></summary></entry><entry><title type="html">No route matches the supplied values</title><link href="https://www.josephguadagno.net/2020/07/01/no-route-matches-the-supplied-values" rel="alternate" type="text/html" title="No route matches the supplied values" /><published>2020-07-01T00:00:00-07:00</published><updated>2020-07-01T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/07/01/no-route-matches-the-supplied-values</id><content type="html" xml:base="https://www.josephguadagno.net/2020/07/01/no-route-matches-the-supplied-values"><![CDATA[<blockquote>
  <p>No route matches the supplied values</p>
</blockquote>

<p>I spent about two hours figuring out why I was getting this error message for the <a href="https://www.github.com/jguadagno/contacts" target="_blank">Contacts API</a>. I followed the <a href="https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/tutorials/first-web-api/samples/3.0" target="_blank">Todo List</a> example by the book so that if someone from the stream were confused, they would have something to fall back on. Unfortunately, it wasn’t totally by the book.</p>

<p>I have code in the API for saving the contact which looks like this:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">ActionResult</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;&gt;</span> <span class="nf">SaveContact</span><span class="p">(</span><span class="n">Contact</span> <span class="n">contact</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">HttpContext</span><span class="p">.</span><span class="nf">VerifyUserHasAnyAcceptedScope</span><span class="p">(</span><span class="n">Domain</span><span class="p">.</span><span class="n">Permissions</span><span class="p">.</span><span class="n">Contacts</span><span class="p">.</span><span class="n">Save</span><span class="p">);</span>
    <span class="kt">var</span> <span class="n">savedContact</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_contactManager</span><span class="p">.</span><span class="nf">SaveContactAsync</span><span class="p">(</span><span class="n">contact</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">savedContact</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="nf">CreatedAtAction</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">GetContactAsync</span><span class="p">),</span>
            <span class="k">new</span> <span class="p">{</span><span class="n">id</span> <span class="p">=</span> <span class="n">contact</span><span class="p">.</span><span class="n">ContactId</span><span class="p">},</span>
            <span class="n">contact</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">return</span> <span class="nf">Problem</span><span class="p">(</span><span class="s">"Failed to insert the contact"</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This code uses the <code class="language-plaintext highlighter-rouge">CreatedAtAction</code> to return the <code class="language-plaintext highlighter-rouge">contact</code> and the URL to view the contact details.  However, this call kept returning an exception <strong><em>No route matches the supplied values</em></strong>. I made sure the method/action existed. Here it is!</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;</span> <span class="nf">GetContactAsync</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">HttpContext</span><span class="p">.</span><span class="nf">VerifyUserHasAnyAcceptedScope</span><span class="p">(</span><span class="n">Domain</span><span class="p">.</span><span class="n">Permissions</span><span class="p">.</span><span class="n">Contacts</span><span class="p">.</span><span class="n">View</span><span class="p">);</span>
    <span class="k">return</span> <span class="k">await</span> <span class="n">_contactManager</span><span class="p">.</span><span class="nf">GetContactAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I double-checked all of the route settings. I double-checked the name of the ‘Get’ function, <code class="language-plaintext highlighter-rouge">GetContactsAsync</code>, and everything looked ok. I even tried to skip the <code class="language-plaintext highlighter-rouge">nameof</code> operator and just hard the <code class="language-plaintext highlighter-rouge">GetContactsAsync</code>, but I kept getting the exception <strong><em>No route matches the supplied values</em></strong></p>

<p>I Googled and Binged for a while and did not come up with anything.  Until I came to this StackOverflow <a href="https://stackoverflow.com/questions/39459348/asp-net-core-web-api-no-route-matches-the-supplied-values" target="_blank">question</a>. The first answer was not the answer that solved my problem, but an answer <a href="https://stackoverflow.com/a/61536687/89184" target="_blank">below</a>.</p>

<p>As the answer suggests, ASP.NET Routing has an issue with the method name ending in the word <code class="language-plaintext highlighter-rouge">Async</code>.  As a result, it could not find my <code class="language-plaintext highlighter-rouge">GetContactsAsync</code> method.</p>

<p>So my options were to rename the method or not use the <code class="language-plaintext highlighter-rouge">CreatedAtAction</code>. I was not too fond of either option. The answer did suggest that I could use the <code class="language-plaintext highlighter-rouge">ActionName</code> attribute to inform the routing of what the actual name of the method is. I’ve updated the method as follows.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="nf">HttpGet</span><span class="p">(</span><span class="s">"{id}"</span><span class="p">)]</span>
<span class="p">[</span><span class="nf">ActionName</span><span class="p">(</span><span class="k">nameof</span><span class="p">(</span><span class="n">GetContactAsync</span><span class="p">))]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">Contact</span><span class="p">&gt;</span> <span class="nf">GetContactAsync</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">HttpContext</span><span class="p">.</span><span class="nf">VerifyUserHasAnyAcceptedScope</span><span class="p">(</span><span class="n">Domain</span><span class="p">.</span><span class="n">Permissions</span><span class="p">.</span><span class="n">Contacts</span><span class="p">.</span><span class="n">View</span><span class="p">);</span>
    <span class="k">return</span> <span class="k">await</span> <span class="n">_contactManager</span><span class="p">.</span><span class="nf">GetContactAsync</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>So using the <code class="language-plaintext highlighter-rouge">ActionName</code> got me over the hurdle of ‘<strong><em>No route matches the supplied values</em></strong>.’ I hope this helps someone.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="WebAPI" /><category term="ASP.NET Core" /><category term="MVC" /><category term="ASP.NET Core MVC" /><summary type="html"><![CDATA[No route matches the supplied values]]></summary></entry><entry><title type="html">Connecting to a Protected API with Microsoft Identity Platform</title><link href="https://www.josephguadagno.net/2020/06/26/connecting-to-an-api-protected-by-microsoft-identity-platform" rel="alternate" type="text/html" title="Connecting to a Protected API with Microsoft Identity Platform" /><published>2020-06-26T00:00:00-07:00</published><updated>2020-06-26T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/06/26/connecting-to-an-api-protected-by-microsoft-identity-platform</id><content type="html" xml:base="https://www.josephguadagno.net/2020/06/26/connecting-to-an-api-protected-by-microsoft-identity-platform"><![CDATA[<p>In a previous <a href="/2020/06/12/protecting-an-asp-net-core-api-with-microsoft-identity-platform">post</a>, I demonstrated how you can protect an ASP.NET Core Web API using the <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Microsoft Identity Platform</a>. In this post, we are going to look at what you need to do to have an ASP.NET Core MVC application interact with the same protected API. It’s really easy, once you figure it out! :wink:</p>

<p class="notice--warning"><strong>Note!</strong> This post was written based on a preview version of the <a href="https://github.com/AzureAD/microsoft-identity-web" target="_blank">Microsoft Identity Web</a> library, version <a href="https://www.nuget.org/packages/Microsoft.Identity.Web/0.1.5-preview" target="_blank">0.1.5-preview</a>. It has been updated to version <a href="https://www.nuget.org/packages/Microsoft.Identity.Web/0.2.0-preview" target="_blank">0.2.0-preview</a> of the library. Your experience may vary!</p>

<p class="notice--info"><strong>Note!</strong> This post assumes that you already have a tenant setup in Azure Active Directory with scopes, similar to the previous <a href="/2020/06/12/protecting-an-asp-net-core-api-with-microsoft-identity-platform">post</a></p>

<h2 id="add-a-new-scope-to-your-api-application-in-azure">Add a New Scope To Your API Application In Azure</h2>

<p>For the new client application, the ASP.NET Core Web application, we will use user impersonation where the Web Application will call the Web API on behalf of the signed-in user. To do this, we need to add a new scope to our API application in Azure.</p>

<p>Navigate to your Azure Active Directory Tenant, click on ‘App Registrations’, and select your API application. In the App Registration for your API, click on ‘Expose an API.’</p>

<p><img src="/assets/images/posts/protect-api-expose-an-api.png" alt="Protect a Web Api - Expose an API" class="align-center" /></p>

<p>Click on ‘+ Add a scope’</p>

<p><img src="/assets/images/posts/protect-api-add-a-scope.png" alt="Protect a Web Api - Add a Scope" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Scope Name</td>
      <td><code class="language-plaintext highlighter-rouge">user_impersonation</code></td>
      <td>This is the name of the scope that you will see in the application code</td>
    </tr>
    <tr>
      <td>Who can consent?</td>
      <td><code class="language-plaintext highlighter-rouge">Admins and Users</code></td>
      <td>Who do you want to offer the scope to?</td>
    </tr>
    <tr>
      <td>Admin consent display name</td>
      <td><code class="language-plaintext highlighter-rouge">Allow User Impersonation</code></td>
      <td>This is what the scope will be called in the consent screen when admins consent to this scope.</td>
    </tr>
    <tr>
      <td>Admin consent description</td>
      <td><code class="language-plaintext highlighter-rouge">Enables the client application to call the Contacts API on your behalf</code></td>
      <td>This is a detailed description of the scope that is displayed when tenant admins expand a scope on the consent screen.</td>
    </tr>
    <tr>
      <td>User consent display name</td>
      <td><code class="language-plaintext highlighter-rouge">Allow User Impersonation</code></td>
      <td>This is what the scope will be called in the consent screen when users consent to this scope.</td>
    </tr>
    <tr>
      <td>User consent description</td>
      <td><code class="language-plaintext highlighter-rouge">Enables the client application to call the Contacts API on your behalf</code></td>
      <td>This is a detailed description of the scope that is displayed when tenant users expand a scope on the consent screen.</td>
    </tr>
    <tr>
      <td>State</td>
      <td><code class="language-plaintext highlighter-rouge">Enabled</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p><strong>NOTE</strong> The <code class="language-plaintext highlighter-rouge">scope name</code>, in this case matters, from what I can tell.  The descriptions are up to you, but these values display to the user when they are giving their consent.</p>

<h3 id="enable-the-new-scope-in-a-client-app-registration">Enable the New Scope in a Client App Registration</h3>

<p>If you followed along with the previous example, you should have an API application and ‘PostMan Client’ application registered. I recommend that you create a Client Registration as outlined in <a href="/2020/06/12/protecting-an-asp-net-core-api-with-microsoft-identity-platform">Register a new client</a> section. In general, it’s a good practice to keep separate ‘client app registrations’ for each client that accesses your API. And by each client, I mean each codebase. With the application I am building, <a href="https://www.github.com/jguadagno/Contacts" target="_blank">Contacts</a>, I have an application registered for the API, the PostMan tests, and not the Contacts MVC application. If I add other clients, like a Blazor application or React-Native application, they will get separate application ids.</p>

<p>Now in that newly created application, go to ‘API permissions.’</p>

<ul>
  <li>Click ‘Add a permission’</li>
  <li>Choose ‘My APIs’</li>
  <li>Choose ‘Contacts API’</li>
</ul>

<p>You should see the new ‘user_impersonation’ permissions under the ‘Permission’ section.</p>

<p><img src="/assets/images/posts/connecting-to-protected-api-request-permission.png" alt="Connecting to a Protected API - Requesting Permissions" class="align-center" /></p>

<ul>
  <li>Check ‘user_impersonation’</li>
  <li>Click ‘Add permissions’</li>
</ul>

<p>When clicking ‘Add permissions’, you will probably see a message at the top of the scopes page indicating that since you change permissions, all users will have to give their consent again.</p>

<h2 id="connecting-to-the-api-from-an-aspnet-core-web-application-mvc">Connecting to the API from an ASP.NET Core Web Application (MVC)</h2>

<p>There are a few different things that you have to do to enable authentication. First thing to do is install the Microsoft Identity Platform NuGet package</p>

<h3 id="microsoftidentityweb-package">Microsoft.Identity.Web Package</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>Install-Package Microsoft.Identity.Web <span class="nt">-Version</span> 0.2.0-preview
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>Note</strong> The latest version number might be different.</p>

<h3 id="application-settings">Application Settings</h3>

<p>Next up, add the Azure Active Directory Settings to your <code class="language-plaintext highlighter-rouge">appsettings.Development.json</code> file.  <strong>Note</strong>, you should not store client secrets or PII in your version repository, Git, SVN, TFS, etc. you should use the ‘<a href="https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-3.1&amp;tabs=windows&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">user secrets</a>’ functionality, <a href="https://azure.microsoft.com/en-us/services/app-configuration/?wt.mc_id=DT-MVP-4024623" target="_blank">Azure App Configuration</a> service, or <a href="https://azure.microsoft.com/en-us/services/key-vault/?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault</a> service.  For now, I will show it the <code class="language-plaintext highlighter-rouge">appsettings.development.json</code>.</p>

<p>You will need to add the <code class="language-plaintext highlighter-rouge">AzureAd</code> section.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nl">"AzureAd"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"Instance"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://login.microsoftonline.com/"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"Domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"replace with your domain. The domain of the AAD tenant"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ClientId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"replace with your Api Application client id"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"TenantId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"common"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>You need specify the TenantId only if you want to accept access tokens from a single-tenant (line-of-business app). Otherwise, you can leave them set to common.</p>

<p>This can be:</p>

<ul>
  <li>A GUID (Tenant ID = Directory ID)</li>
  <li>‘common’ (any organization and personal accounts)</li>
  <li>‘organizations’ (any organization)</li>
  <li>‘consumers’ (Microsoft personal accounts)</li>
</ul>

<h3 id="startupcs">Startup.cs</h3>

<p>Now in your <code class="language-plaintext highlighter-rouge">Startup.cs</code> file, you are going to need to enable the Microsoft Identity Platform. In the <code class="language-plaintext highlighter-rouge">ConfigureServices</code> method, add as one of the first lines in the method</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="rouge-code"><pre><span class="n">services</span><span class="p">.</span><span class="nf">AddSignIn</span><span class="p">(</span><span class="n">Configuration</span><span class="p">);</span>

<span class="kt">var</span> <span class="n">initialScopes</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span>
<span class="p">{</span>
    <span class="c1">// A full string array of the scopes you want the user to consent to...</span>
    <span class="s">"api://dc68a11f-d265-4e9c-8a24-abbbd3123456/Scope1"</span><span class="p">,</span>
    <span class="s">"api://dc68a11f-d265-4e9c-8a24-abbbd3123456/Scope2"</span>
<span class="p">};</span>
<span class="c1">// Token acquisition service based on MSAL.NET</span>
<span class="c1">// and chosen token cache implementation</span>
<span class="n">services</span><span class="p">.</span><span class="nf">AddMicrosoftWebAppAuthentication</span><span class="p">(</span><span class="n">Configuration</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">AddMicrosoftWebAppCallsWebApi</span><span class="p">(</span><span class="n">Configuration</span><span class="p">,</span> <span class="n">initialScopes</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">AddInMemoryTokenCaches</span><span class="p">();</span>

<span class="n">services</span><span class="p">.</span><span class="nf">AddControllersWithViews</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="kt">var</span> <span class="n">policy</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AuthorizationPolicyBuilder</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">RequireAuthenticatedUser</span><span class="p">()</span>
        <span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
    <span class="n">options</span><span class="p">.</span><span class="n">Filters</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">AuthorizeFilter</span><span class="p">(</span><span class="n">policy</span><span class="p">));</span>
<span class="p">}).</span><span class="nf">AddMicrosoftIdentityUI</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="controller">Controller</h3>

<p>Now in the controller you want to add authorization to, add the <code class="language-plaintext highlighter-rouge">Authorize</code> attribute to the controller.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="n">Authorize</span><span class="p">]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">ContactController</span> <span class="p">:</span> <span class="n">Controller</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then for each controller action you want authentication for, add the <code class="language-plaintext highlighter-rouge">AuthorizeForScopes</code> attribute like this</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="c1">// GET</span>
<span class="p">[</span><span class="nf">AuthorizeForScopes</span><span class="p">(</span><span class="n">Scopes</span> <span class="p">=</span> <span class="k">new</span> <span class="p">[]{</span><span class="s">"api://dc68a11f-d265-4e9c-8a24-abbbd3123456/Scope1"</span><span class="p">})]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">Index</span><span class="p">()</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You don’t have to hard code the permission here. There is a parameter that allows you to read the scope from the application configuration. In this case, you will add the same <code class="language-plaintext highlighter-rouge">AuthorizeForScopes</code> attribute and use the <code class="language-plaintext highlighter-rouge">ScopeKeySection</code>.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="c1">// GET</span>
<span class="p">[</span><span class="nf">AuthorizeForScopes</span><span class="p">(</span><span class="n">ScopeKeySection</span> <span class="p">=</span> <span class="s">"Contacts:ContactsScope"</span><span class="p">))]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">Index</span><span class="p">()</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Inside the method for the controller, <code class="language-plaintext highlighter-rouge">Index</code>, in this example you are going to need to:</p>

<ol>
  <li>Get an access token for the given scope</li>
  <li>Add the <code class="language-plaintext highlighter-rouge">Bearer</code> token to the header</li>
  <li>Make the API call</li>
</ol>

<p>The full <code class="language-plaintext highlighter-rouge">Index</code> method</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
</pre></td><td class="rouge-code"><pre><span class="c1">// GET</span>
<span class="p">[</span><span class="nf">AuthorizeForScopes</span><span class="p">(</span><span class="n">Scopes</span> <span class="p">=</span> <span class="k">new</span> <span class="p">[]{</span><span class="s">"api://dc68a11f-d265-4e9c-8a24-abbbd3520f8a/"</span> <span class="p">+</span> <span class="n">Domain</span><span class="p">.</span><span class="n">Permissions</span><span class="p">.</span><span class="n">Contacts</span><span class="p">.</span><span class="n">List</span><span class="p">})]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">IActionResult</span><span class="p">&gt;</span> <span class="nf">Index</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Step 1: Check for Scope</span>
    <span class="kt">string</span><span class="p">[]</span> <span class="n">scopes</span> <span class="p">=</span> <span class="p">{</span><span class="s">"api://dc68a11f-d265-4e9c-8a24-abbbd3520f8a/"</span> <span class="p">+</span> <span class="n">Domain</span><span class="p">.</span><span class="n">Permissions</span><span class="p">.</span><span class="n">Contacts</span><span class="p">.</span><span class="n">List</span><span class="p">};</span>
    <span class="kt">string</span> <span class="n">accessToken</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_tokenAcquisition</span><span class="p">.</span><span class="nf">GetAccessTokenForUserAsync</span><span class="p">(</span><span class="n">scopes</span><span class="p">);</span>

    <span class="c1">// Step 2: Build the HttpClient</span>
    <span class="n">Client</span><span class="p">.</span><span class="n">DefaultRequestHeaders</span><span class="p">.</span><span class="n">Authorization</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">AuthenticationHeaderValue</span><span class="p">(</span><span class="s">"Bearer"</span><span class="p">,</span> <span class="n">accessToken</span><span class="p">);</span>
    <span class="n">Client</span><span class="p">.</span><span class="n">DefaultRequestHeaders</span><span class="p">.</span><span class="n">Accept</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">MediaTypeWithQualityHeaderValue</span><span class="p">(</span><span class="s">"application/json"</span><span class="p">));</span>
    <span class="c1">// Step 3: Make the API Call</span>
    <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="n">Client</span><span class="p">.</span><span class="nf">GetAsync</span><span class="p">(</span><span class="s">"https://localhost:5001/contacts"</span><span class="p">);</span>

    <span class="c1">// Every below this line is how I processed the request.</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">StatusCode</span> <span class="p">==</span> <span class="n">HttpStatusCode</span><span class="p">.</span><span class="n">OK</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">// Parse the Results</span>
        <span class="kt">var</span> <span class="n">content</span> <span class="p">=</span> <span class="k">await</span> <span class="n">response</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">ReadAsStringAsync</span><span class="p">();</span>
        <span class="kt">var</span> <span class="n">options</span> <span class="p">=</span> <span class="k">new</span> <span class="n">JsonSerializerOptions</span>
        <span class="p">{</span>
            <span class="n">PropertyNameCaseInsensitive</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
        <span class="p">};</span>
        <span class="n">List</span><span class="p">&lt;</span><span class="n">Domain</span><span class="p">.</span><span class="n">Models</span><span class="p">.</span><span class="n">Contact</span><span class="p">&gt;</span> <span class="n">contacts</span> <span class="p">=</span>
            <span class="n">JsonSerializer</span><span class="p">.</span><span class="n">Deserialize</span><span class="p">&lt;</span><span class="n">List</span><span class="p">&lt;</span><span class="n">Domain</span><span class="p">.</span><span class="n">Models</span><span class="p">.</span><span class="n">Contact</span><span class="p">&gt;&gt;(</span><span class="n">content</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>

        <span class="k">return</span> <span class="nf">View</span><span class="p">(</span><span class="n">contacts</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nf">HttpRequestException</span><span class="p">(</span><span class="s">$"Invalid status code in the HttpResponseMessage: </span><span class="p">{</span><span class="n">response</span><span class="p">.</span><span class="n">StatusCode</span><span class="p">}</span><span class="s">."</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You can see the example project at the point all this worked on <a href="https://github.com/jguadagno/Contacts/commit/7eb353b13ca1a0b135e1a777c718f96bdb353025">Github</a></p>

<h4 id="gotchas">Gotchas</h4>

<p>Working with a preview of any product can be challenging, here are some things I ran into and what fixed it.</p>

<h5 id="no-account-or-login-hint-was-passed-to-the-acquiretokensilent-call">No account or login hint was passed to the AcquireTokenSilent call</h5>

<p>Exception with the text of <code class="language-plaintext highlighter-rouge">no account or login hint was passed to the AcquireTokenSilent call.</code>  This message generally means that the token or permissions are out of sync. Typically the user needs to provide new consents, or the token expires.</p>

<p>Fix 1: Clear your cookies in whatever web browser you are using an try again.</p>

<p>Fix 2: Check to ensure that the AzureAd configuration has the correct <code class="language-plaintext highlighter-rouge">TenantId</code>, <code class="language-plaintext highlighter-rouge">ClientId</code>, and <code class="language-plaintext highlighter-rouge">ClientSecret</code>.</p>

<p>Fix 3: Implement the changes in the ‘Login Changes’ section below.</p>

<h5 id="message-contains-error-invalid_client-error_description-aadsts650053">Message contains error: ‘invalid_client’ error_description: ‘AADSTS650053’</h5>

<p>The full messages is something like</p>

<blockquote>
  <p>OpenIdConnectProtocolException: Message contains error: ‘invalid_client’, error_description: ‘AADSTS650053: The application ‘<em>Application Name</em>’ asked for scope ‘<em>Scope Name</em>’ that doesn’t exist on the resource ‘00000003-0000-0000-c000-000000000000’. Contact the app vendor.</p>
</blockquote>

<p>Where <em>Application Name</em> is your application name (as known in Azure Active Directory) and <em>Scope Name</em> is the scope you have defined.</p>

<p>This is due to you <strong>NOT</strong> having the fully qualified Scope Name in the <code class="language-plaintext highlighter-rouge">AuthorizeForScopes</code> attributes.  The <code class="language-plaintext highlighter-rouge">Scopes</code> parameter should be the fully qualified name with the app uri as defined in the Azure App Registration for your API.  In this sample I had.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="nf">AuthorizeForScopes</span><span class="p">(</span><span class="n">Scopes</span> <span class="p">=</span> <span class="k">new</span> <span class="p">[]{</span><span class="s">"Contacts.List"</span><span class="p">})]</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>instead of</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="nf">AuthorizeForScopes</span><span class="p">(</span><span class="n">Scopes</span> <span class="p">=</span> <span class="k">new</span> <span class="p">[]{</span><span class="s">"api://dc68a11f-d265-4e9c-8a24-abbbd3520f8a/Contacts.List"</span><span class="p">})]</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="login-changes">Login Changes</h3>

<p>When you have authentication enabled, the out-of-the-box ASP.NET Core MVC templates use the older Azure Active Directory package of <code class="language-plaintext highlighter-rouge">Microsoft.AspNetCore.Authentication.AzureAD.UI</code>. This package is safe to remove with respect to this sample. However, there is a small change to the <code class="language-plaintext highlighter-rouge">_LoginPartial.cshtml</code> that needs to happen. The <code class="language-plaintext highlighter-rouge">SignIn</code>\<code class="language-plaintext highlighter-rouge">SignOut</code> components of the <code class="language-plaintext highlighter-rouge">_LoginPartial.cshtml</code> page <del>needs</del> to change.  So look for</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"nav-link text-dark"</span> <span class="na">asp-area=</span><span class="s">"AzureAD"</span> <span class="na">asp-controller=</span><span class="s">"Account"</span> <span class="na">asp-action=</span><span class="s">"SignOut"</span><span class="nt">&gt;</span>Sign out<span class="nt">&lt;/a&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>and</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"nav-link text-dark"</span> <span class="na">asp-area=</span><span class="s">"AzureAD"</span> <span class="na">asp-controller=</span><span class="s">"Account"</span> <span class="na">asp-action=</span><span class="s">"SignIn"</span><span class="nt">&gt;</span>Sign in<span class="nt">&lt;/a&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>and change the <code class="language-plaintext highlighter-rouge">asp-area</code> to <code class="language-plaintext highlighter-rouge">MicrosoftIdentity</code></p>

<p>If you used the template as-is, your <code class="language-plaintext highlighter-rouge">_LoginPartial.cshtml</code> should look like this after the change.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="rouge-code"><pre>@using Microsoft.Identity.Web

<span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">"navbar-nav"</span><span class="nt">&gt;</span>
    @if (User.Identity.IsAuthenticated)
    {
        <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"navbar-text text-dark"</span><span class="nt">&gt;</span>Hello @User.GetDisplayName()!<span class="nt">&lt;/span&gt;</span>
        <span class="nt">&lt;/li&gt;</span>
        <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"nav-link text-dark"</span> <span class="na">asp-area=</span><span class="s">"MicrosoftIdentity"</span> <span class="na">asp-controller=</span><span class="s">"Account"</span> <span class="na">asp-action=</span><span class="s">"SignOut"</span><span class="nt">&gt;</span>Sign out<span class="nt">&lt;/a&gt;</span>
        <span class="nt">&lt;/li&gt;</span>
    }
    else
    {
        <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"nav-item"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"nav-link text-dark"</span> <span class="na">asp-area=</span><span class="s">"MicrosoftIdentity"</span> <span class="na">asp-controller=</span><span class="s">"Account"</span> <span class="na">asp-action=</span><span class="s">"SignIn"</span><span class="nt">&gt;</span>Sign in<span class="nt">&lt;/a&gt;</span>
        <span class="nt">&lt;/li&gt;</span>
    }
<span class="nt">&lt;/ul&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="wrap-up">Wrap up</h2>

<p>I’ll continue to update this post as I add other clients to the Contacts application.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Identity" /><category term="WebAPI" /><category term="ASP.NET Core" /><category term="MVC" /><category term="ASP.NET Core MVC" /><summary type="html"><![CDATA[In a previous post, I demonstrated how you can protect an ASP.NET Core Web API using the Microsoft Identity Platform. In this post, we are going to look at what you need to do to have an ASP.NET Core MVC application interact with the same protected API. It’s really easy, once you figure it out! :wink:]]></summary></entry><entry><title type="html">Speed up Creating Code with Live Templates in JetBrains Rider or ReSharper</title><link href="https://www.josephguadagno.net/2020/06/14/speed-up-creating-code-with-live-templates-in-jetbrains-rider-or-resharper" rel="alternate" type="text/html" title="Speed up Creating Code with Live Templates in JetBrains Rider or ReSharper" /><published>2020-06-14T00:00:00-07:00</published><updated>2020-06-14T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/06/14/speed-up-creating-code-with-live-templates-in-jetbrains-rider-or-resharper</id><content type="html" xml:base="https://www.josephguadagno.net/2020/06/14/speed-up-creating-code-with-live-templates-in-jetbrains-rider-or-resharper"><![CDATA[<p>For the past few weekends, I’ve been working rebuilding a library I wrote to make it easier to use Azure Storage. I started this library about 4 or 5 years ago, and it did not have ‘true’ unit tests. I’m not going to talk about what makes a good unit test and what doesn’t. There are plenty of posts around that! I decided to rewrite all of the tests and move to <a href="https://xUnit.net" target="_blank">xUnit</a>. After creating about the 50th test or so, I noticed a pattern in the initial shell of the tests. Most of the tests looked like this.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="n">Fact</span><span class="p">]</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">MethodName_WithSomething_ShouldDoSomething</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Arrange</span>

    <span class="c1">// Act</span>

    <span class="c1">// Assert</span>
    <span class="n">Assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="k">false</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I’ve been a user of <a href="https://www.jetbrains.com" target="_blank">JetBrains</a> <a href="https://jetbrains.com/resharper" target="_blank">ReSharper</a> (a Visual Studio extension that makes Visual Studio better) for 9 or so years, maybe longer, and a user of <a href="https://www.jetbrains.com" target="_blank">JetBrains</a> <a href="https://jetbrains.com/rider" target="_blank">Rider</a> (cross platform .NET IDE), for about 3 months now, and they both have a feature called <a href="https://www.jetbrains.com/help/rider/using-live-templates.html" target="_blank">Live Templates</a>. Live templates provide you with the ability to create templates for code you often type. These are similar to snippets in Visual Studio but stronger. So to I solved my repeating the same code to start a unit tests with a live code template.</p>

<h2 id="viewing-existing-templates">Viewing Existing Templates</h2>

<p>How you get to viewing or creating the existing templates varies depending on whether you are on a Mac or a Windows and using ReSharper or Rider.  For both Rider and ReSharper, Live Templates are available in the settings/options.</p>

<p>Here is what the Live Template settings looks like.</p>

<p><img src="/assets/images/posts/live-templates-settings.png" alt="Speed up code - Live Template Settings" class="align-center" /></p>

<h3 id="template-dialog-box-explained">Template Dialog Box Explained</h3>

<p><img src="/assets/images/posts/live-templates-settings-annotated.png" alt="Speed up code - Live Template Settings Annotated" class="align-center" /></p>

<p>The ‘Template List’ is where you will find all of the templates for the given language.</p>

<p>Clicking on a template will show you the template in the area I named ‘Template’. I know original naming :smile:. Ignore the dollar signs ($) for a second.</p>

<p>The ‘Template Settings’ displays the settings for the template. I know I’m being original again :smile:.</p>

<h2 id="creating-a-new-template">Creating a New Template</h2>

<p>To create a template, you will need to click on the ‘New Template’ icon in the upper right-hand corner, the one with a star on the table-like grid (follow the arrow in the image above). Make sure you are in the language of choice first. That’s right, and you can create a template for any language that Rider/ReSharper support. Oh, as a bonus, you can edit existing ones if you prefer. As another bonus, if you have Preference Sync turned on, the template changes will be available in all instances of your application on any device.</p>

<p>After you click new, the ‘Template’ and ‘Template Settings’ will be set to the defaults. For my <code class="language-plaintext highlighter-rouge">fact</code> template, I added the following for the template.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="n">Fact</span><span class="p">]</span>
<span class="k">public</span> <span class="k">void</span> <span class="err">$</span><span class="n">MethodName</span><span class="err">$</span><span class="n">_With</span><span class="err">$</span><span class="n">What</span><span class="err">$</span><span class="n">_Should</span><span class="err">$</span><span class="n">DoWhat</span><span class="err">$</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Arrange</span>
    <span class="err">$</span><span class="n">END</span><span class="err">$</span>
    <span class="c1">// Act</span>

    <span class="c1">// Assert</span>
    <span class="n">Assert</span><span class="p">.</span><span class="nf">True</span><span class="p">(</span><span class="k">false</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>What are those dollar signs (<code class="language-plaintext highlighter-rouge">$</code>) for you ask? Well, I am glad you asked. The dollar signs are used for variables/inputs/prompts for your template. In this example, I have 4, <code class="language-plaintext highlighter-rouge">$MethodName$</code>, <code class="language-plaintext highlighter-rouge">$What$</code>, <code class="language-plaintext highlighter-rouge">$DoWhat$</code>, and <code class="language-plaintext highlighter-rouge">$END$</code>. So, in Rider/Visual Studio, when I enable the template, I will be prompted inline for the <code class="language-plaintext highlighter-rouge">$MethodName$</code>, then <code class="language-plaintext highlighter-rouge">$What$</code>, then <code class="language-plaintext highlighter-rouge">$DoWhat$</code>. The <code class="language-plaintext highlighter-rouge">$END$</code> indicates where the editing caret should end when you are complete with editing all the variables. The names matter as much as naming your methods or variables in code. They should reference what they are doing or expecting. For my unit test, the first variable is <code class="language-plaintext highlighter-rouge">MethodName</code>, which is the method I am testing, followed by <code class="language-plaintext highlighter-rouge">What</code> the tests expect as input, then followed up with the test should <code class="language-plaintext highlighter-rouge">DoWhat</code>, and the template <code class="language-plaintext highlighter-rouge">End</code>s in a position where I can start arranging the tests.</p>

<p>Now, I can expand this a little bit more with some ‘smarts’ from the IDE by adding <a href="https://www.jetbrains.com/help/idea/template-variables.html#predefined_functions" target="_blank">live template functions</a> but that maybe another blog posts.</p>

<h3 id="template-settings">Template Settings</h3>

<p>This is where you can tweak the settings a bit</p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Used For</td>
    </tr>
    <tr>
      <td>Shortcut</td>
      <td>This is what you type in the editor to use this snippet. This also appears in the Intellisense lists</td>
    </tr>
    <tr>
      <td>Description</td>
      <td>A description of the template. This is helpful to explain what the template does for others</td>
    </tr>
    <tr>
      <td>Use in</td>
      <td><code class="language-plaintext highlighter-rouge">Generation</code> means the creation of new code. <code class="language-plaintext highlighter-rouge">Surround</code> is used when you have selected code that you want to surround, like adding a try/catch.</td>
    </tr>
    <tr>
      <td>Mnemonic</td>
      <td>If your template name is long, you can specify what letters to use to select it.</td>
    </tr>
  </tbody>
</table>

<h2 id="wrap-up">Wrap Up</h2>

<p>There are a lot of things that you can do with live templates. I urge you to take a look at them.  I know they have saved me some time, except for writing this blog post :smile:.</p>

<h3 id="further-resources">Further Resources</h3>

<ul>
  <li><a href="https://www.jetbrains.com/help/rider/Templates_Rider.html" target="_blank">Using Templates to Create Code</a></li>
  <li><a href="https://www.jetbrains.com/help/rider/Sharing_Live_Templates.html" target="_blank">Share Live Templates</a></li>
  <li><a href="https://www.jetbrains.com/help/rider/template_variables.html" target="_blank">Live Template Variables</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="JetBrains" /><category term="Rider" /><category term="ReSharper" /><category term="Productivity" /><category term="Unit Tests" /><summary type="html"><![CDATA[For the past few weekends, I’ve been working rebuilding a library I wrote to make it easier to use Azure Storage. I started this library about 4 or 5 years ago, and it did not have ‘true’ unit tests. I’m not going to talk about what makes a good unit test and what doesn’t. There are plenty of posts around that! I decided to rewrite all of the tests and move to xUnit. After creating about the 50th test or so, I noticed a pattern in the initial shell of the tests. Most of the tests looked like this.]]></summary></entry><entry><title type="html">Protecting an ASP.NET Core Web API with Microsoft Identity Platform</title><link href="https://www.josephguadagno.net/2020/06/12/protecting-an-asp-net-core-api-with-microsoft-identity-platform" rel="alternate" type="text/html" title="Protecting an ASP.NET Core Web API with Microsoft Identity Platform" /><published>2020-06-12T19:06:00-07:00</published><updated>2025-02-22T19:06:00-07:00</updated><id>https://www.josephguadagno.net/2020/06/12/protecting-an-asp-net-core-api-with-microsoft-identity-platform</id><content type="html" xml:base="https://www.josephguadagno.net/2020/06/12/protecting-an-asp-net-core-api-with-microsoft-identity-platform"><![CDATA[<p><a href="https://www.nuget.org/packages/Microsoft.Identity.Web" target="_blank">Microsoft Identity Web</a> is a library which contains a set of reusable classes used in conjunction with ASP.NET Core for integrating with the <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/?wt.mc_id=DT-MVP-4024623" target="_blank">Microsoft identity platform</a> (formerly <em>Azure AD v2.0 endpoint</em>) and <a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/?wt.mc_id=DT-MVP-4024623" target="_blank">AAD B2C</a>.</p>

<p>This library is for specific usage with:</p>

<ul>
  <li><a href="https://github.com/AzureAD/microsoft-identity-web/wiki/web-apps" target="_blank">Web applications</a>, which sign in users and, optionally, call web APIs</li>
  <li><a href="https://github.com/AzureAD/microsoft-identity-web/wiki/web-apis" target="_blank">Protected web APIs</a>, which optionally call protected downstream web APIs</li>
</ul>

<p class="notice--warning"><strong>Note!</strong> This post was written based on a preview version of the <a href="https://github.com/AzureAD/microsoft-identity-web" target="_blank">Microsoft Identity Web</a> library, version <a href="https://www.nuget.org/packages/Microsoft.Identity.Web/0.1.4-preview" target="_blank">0.1.4-preview</a>. It has been updated to version <a href="https://www.nuget.org/packages/Microsoft.Identity.Web/0.2.0-preview" target="_blank">0.2.0-preview</a>. Your experience may vary!</p>

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>An Azure account with an active subscription. <a href="https://azure.microsoft.com/free/?ref=microsoft.com&amp;utm_source=microsoft.com&amp;utm_medium=docs&amp;utm_campaign=visualstudio&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Create an account for free</a>.</li>
  <li>An <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-create-new-tenant?wt.mc_id=DT-MVP-4024623" target="_blank">Azure AD tenant</a>.</li>
  <li>An ASP.NET Core Web Api.  If you need one, you can start with the <a href="https://www.github.com/jguadagno/contacts" target="_blank">Contacts</a> application at this <a href="https://github.com/jguadagno/Contacts/commit/e34ff477146c336a47eae141de31cede197650cf" target="_blank">commit</a>.</li>
</ul>

<h2 id="getting-started-with-microsoft-identity-platform">Getting Started with Microsoft Identity Platform</h2>

<p>The code for this is ridiculously simple, as that was the intention. However, if you are starting without the infrastructure, like I was, there are a couple of things to do.</p>

<h3 id="creating-the-azure-active-directory-aad-tenant">Creating the Azure Active Directory (AAD) Tenant</h3>

<p>First, log into the Azure Portal.</p>

<ul>
  <li>Click on ‘Create a resource’</li>
  <li>In the search box, type <code class="language-plaintext highlighter-rouge">Azure Active Directory</code> and select it</li>
  <li>Click on ‘Create’</li>
</ul>

<p><img src="/assets/images/posts/protect-api-create-tenant.png" alt="Azure Active Directory - Create Tenant" class="align-center" /></p>

<p>Enter an Organization name. The organization name is required and should be something that relates to your organization.</p>

<p>Next, enter the initial domain name. This domain name is a subdomain of onmicrosoft.com. You can add a custom domain like josephguadagno.net later on in the process. We won’t be doing that as part of this post.</p>

<p>Select your country and click ‘Create’. Once the directory is created, you will see a message similar to this.</p>

<p><img src="/assets/images/posts/protect-api-tenant-created.png" alt="Azure Active Directory - Tenant Created" class="align-center" /></p>

<h3 id="registering-applications-with-the-directory">Registering Applications with the Directory</h3>

<p>Now that we have an Azure Active Directory (AAD) tenant created, we will have to have register at least two applications within the portal, an application that represents the API we are protecting, and an application that will represent the client. The reason I said at least two is because you should create one client for each client you will consume/call your API. An example of a client could be a Windows desktop application, developer application (Visual Studio, Postman, etc.), or other Apis. In this post, we are only going to create on the client to connect to our API.</p>

<h4 id="register-the-api-application">Register the Api Application</h4>

<p>This application is used by your ASP.NET Core Web API to check the client’s authentication.</p>

<p>Back in the Azure portal for your tenant, click on ‘App Registration’, and then click on ‘+ New Registration’</p>

<p><img src="/assets/images/posts/protect-api-create-application.png" alt="Azure Active Directory - Register Application" class="align-center" /></p>

<ul>
  <li>Enter a name, I typically choose the name of the application and ‘Api’. Example: <code class="language-plaintext highlighter-rouge">Contacts Api</code></li>
  <li>Choose who can access your application.  For this post I am choosing the option that allows anyone that has a Azure AD Account or consumers account (Xbox, Skype, etc) access to the Api.  That is <code class="language-plaintext highlighter-rouge">Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)</code></li>
  <li>Platform configuration, choose <code class="language-plaintext highlighter-rouge">Web API</code></li>
  <li>Click ‘Register’</li>
</ul>

<p>Once the application is registered you will be brought to the <strong>API permissions</strong> page. At this point, you can delete the default ‘Microsoft Graph’ permissions by clicking on the three dots.  We will not be using them.</p>

<h4 id="register-the-client-application">Register the Client Application</h4>

<p>Go back to your tenant. You should be able to click on the name of the tenant towards the top of the page. Click on ‘App Registrations’ and then click on ‘+ New Registration’</p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Name</td>
      <td><code class="language-plaintext highlighter-rouge">Postman Client</code></td>
      <td>This is the name of this client.  It should be something that is clear for you. You may be adding more than one</td>
    </tr>
    <tr>
      <td>Supported account types</td>
      <td>Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)</td>
      <td>For more details on account types please visit <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app#register-a-new-application-using-the-azure-portal" target="_blank">Register a new application using the Azure portal</a></td>
    </tr>
    <tr>
      <td>Platform configuration</td>
      <td><code class="language-plaintext highlighter-rouge">Client Application (Web, iOS, Android, Desktop+Devices)</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Click ‘Register’</li>
</ul>

<p>Because we chose, <em>Client Application</em>, for the platform configuration, we are directed to the ‘Authentication’ blade after the creation of the application.</p>

<ul>
  <li>Click ‘+ Add a platform’</li>
</ul>

<p>For this post, we are going to be using <a href="https://www.getpostman.com" target="_blank">Postman</a> for validating our Api authentication so I am choosing ‘Web’.</p>

<ul>
  <li>Click ‘Web’</li>
</ul>

<p>For the Redirect URIs enter <code class="language-plaintext highlighter-rouge">https://www.postman.com/oauth2/callback</code>. Note, none of the other options are required.</p>

<ul>
  <li>Click ‘Configure’</li>
</ul>

<p>Applications are registered!  Next step, create the scopes.</p>

<h3 id="creating-scopes">Creating Scopes</h3>

<p>Scope is a mechanism in OAuth 2.0 to limit an application’s access to a user’s account. An application can request one or more scopes, this information is then presented to the user in the consent screen, and the access token issued to the application will be limited to the scopes granted.</p>

<p>For this sample, we will create one scope to use and enable in our application.</p>

<p>In the Azure portal, go to the API application registration and click on ‘Expose an API’.  You’ll see something similar to this.</p>

<p><img src="/assets/images/posts/protect-api-expose-an-api.png" alt="Protect an Api - Expose an Api" class="align-center" /></p>

<p>Where you see the ‘Application ID URI’, click ‘Set’. You’ll something like <code class="language-plaintext highlighter-rouge">api://747cc384-f337-4138-a25b-6b38664404a5</code>. This is used as the base of your scopes, the Guid is automatically generated.  Click ‘Save’. Copy this somewhere, we’ll need it later.</p>

<p>Next, we’ll add one or more scopes.  You’ll want one scope defined for every permission you want.  If you think of it from an operating systems file stand point, one permission to read, one to write, one to delete, etc.  Click on ‘+ Add a scope’.</p>

<p><img src="/assets/images/posts/protect-api-add-a-scope.png" alt="Protect an Api - Add a Scope" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Scope Name</td>
      <td><code class="language-plaintext highlighter-rouge">Contact.View</code></td>
      <td>This is the name of the scope that you will see in the application code</td>
    </tr>
    <tr>
      <td>Who can consent?</td>
      <td><code class="language-plaintext highlighter-rouge">Admins and Users</code></td>
      <td>Who do you want to offer the scope to?</td>
    </tr>
    <tr>
      <td>Admin consent display name</td>
      <td><code class="language-plaintext highlighter-rouge">View Contacts</code></td>
      <td>This is what the scope will be called in the consent screen when admins consent to this scope.</td>
    </tr>
    <tr>
      <td>Admin consent description</td>
      <td><code class="language-plaintext highlighter-rouge">View all of the contacts and their details</code></td>
      <td>This is a detailed description of the scope that is displayed when tenant admins expand a scope on the consent screen.</td>
    </tr>
    <tr>
      <td>User consent display name</td>
      <td><code class="language-plaintext highlighter-rouge">View Contacts</code></td>
      <td>This is what the scope will be called in the consent screen when users consent to this scope.</td>
    </tr>
    <tr>
      <td>User consent description</td>
      <td><code class="language-plaintext highlighter-rouge">View all of the contacts and their details</code></td>
      <td>This is a detailed description of the scope that is displayed when tenant users expand a scope on the consent screen.</td>
    </tr>
    <tr>
      <td>State</td>
      <td><code class="language-plaintext highlighter-rouge">Enabled</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>You’ll notice, beneath the text box for the scope name it is creating and id something like this: <code class="language-plaintext highlighter-rouge">api://747cc384-f337-4138-a25b-6b38664404a5/Contact.View</code>. This is a concatenation of your Application Id Uri and the scope name.</p>

<ul>
  <li>Click ‘Add Scope’</li>
</ul>

<p>You would repeat these steps for each scope you want in your application</p>

<h4 id="enabling-scopes-for-client-application">Enabling Scopes for Client Application</h4>

<p>Now that we have a scope, we need to give our client application the permissions to use that scope.</p>

<p>Go to the App Registration for the Client application in the Azure portal and click on ‘API Permissions’.</p>

<ul>
  <li>Click on ‘+ Add a permission’</li>
</ul>

<p><img src="/assets/images/posts/protect-api-request-api-permissions.png" alt="Protect an Api - Request API Permissions" class="align-center" /></p>

<ul>
  <li>Click on ‘My APIs’</li>
  <li>Select your Api: <code class="language-plaintext highlighter-rouge">Contacts Api</code></li>
  <li>Select <code class="language-plaintext highlighter-rouge">Delegated permissions</code></li>
  <li>Expand the ‘Contact’ list and select <code class="language-plaintext highlighter-rouge">Contact.View</code></li>
  <li>Click ‘Add Permissions’</li>
</ul>

<h3 id="getting-clients-ids-and-secrets">Getting Clients Ids and Secrets</h3>

<p>We are going to need a few more things from the Azure portal for the ASP.NET Core Web API so let’s get them while we are here.</p>

<h4 id="client-app-registration">Client App Registration</h4>

<p>In the Client App Registration, ‘Postman Client’, click on ‘Certificates &amp; secrets’.</p>

<ul>
  <li>Click ‘+ New client secret’</li>
</ul>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Description</td>
      <td><code class="language-plaintext highlighter-rouge">Client Secret</code></td>
      <td>A description for the secret</td>
    </tr>
    <tr>
      <td>Expires</td>
      <td><code class="language-plaintext highlighter-rouge">Never</code></td>
      <td>This is up to you for this post I chose ‘Never’ but I am deleting the app shortly</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Click ‘Add’</li>
</ul>

<p class="notice--warning"><strong>Note!</strong> Copy this secret somewhere safe!  You cannot retrieve it one you leave this blade!  You will have to recreate it if you do not save it.</p>

<ul>
  <li>Click on ‘Overview’ in the menu, and copy the ‘Application (client) ID’</li>
</ul>

<h4 id="api-application-registration">Api Application Registration</h4>

<p>In the App Registration for the API Application, click on ‘Overview’ and copy</p>

<ul>
  <li>‘Application (client) ID’</li>
  <li>‘Directory (tenant) ID’</li>
</ul>

<p>Click on ‘Endpoints’ and copy</p>

<ul>
  <li>OAuth 2.0 authorization endpoint (v2)</li>
  <li>OAuth 2.0 token endpoint (v2)</li>
</ul>

<p>That should be it for the portal.  On to the application.</p>

<h3 id="aspnet-core-web-api-application">ASP.NET Core Web API application</h3>

<p>You’ll need to make 5 changes to your ASP.NET Core Web API. First step is to install the <a href="https://www.nuget.org/packages/Microsoft.Identity.Web" target="_blank">Microsoft Identity Web</a>package in your API package.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>Install-Package Microsoft.Identity.Web <span class="nt">-Version</span> 0.2.0-preview
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>Note</strong> The version number might be different.</p>

<p>Now in your appsettings.json you will need to add the <code class="language-plaintext highlighter-rouge">AzureAd</code> section</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nl">"AzureAd"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"Instance"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://login.microsoftonline.com/"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"Domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"replace with your domain. The domain of the AAD tenant"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ClientId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"replace with your Api Application client id"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"TenantId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"common"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>You need specify the TenantId only if you want to accept access tokens from a single tenant (line-of-business app). Otherwise, you can leave them set to common.</p>

<p>This can be:</p>

<ul>
  <li>A GUID (Tenant ID = Directory ID)</li>
  <li>‘common’ (any organization and personal accounts)</li>
  <li>‘organizations’ (any organization)</li>
  <li>‘consumers’ (Microsoft personal accounts)</li>
</ul>

<p>Now in your <code class="language-plaintext highlighter-rouge">Startup.cs</code> file, you are going to need to enable the Microsoft Identity Platform. In the <code class="language-plaintext highlighter-rouge">ConfigureServices</code> method, add as one of the first lines in the method</p>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">services</span><span class="p">.</span><span class="nf">AddMicrosoftWebApiAuthentication</span><span class="p">(</span><span class="n">Configuration</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This enables the platform.</p>

<p>Next, in the <code class="language-plaintext highlighter-rouge">Configure</code> method, add the following.</p>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">app</span><span class="p">.</span><span class="nf">UseAuthentication</span><span class="p">();</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseAuthorization</span><span class="p">();</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now we need to add the <code class="language-plaintext highlighter-rouge">Authorize</code> attribute to any controllers that we want to <em>protect</em>. In our sample application, we open the <code class="language-plaintext highlighter-rouge">ContactsController</code> and add to the top the <code class="language-plaintext highlighter-rouge">Authorize</code> attribute.  It should look something like this.</p>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// The contacts endpoints provide the functionality to maintain our contacts.</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="p">[</span><span class="n">Authorize</span><span class="p">]</span>
<span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"[controller]"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">ContactsController</span><span class="p">:</span> <span class="n">Controller</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Finally, we need to add the following code to any methods that we want to check permissions for.</p>

<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">HttpContext</span><span class="p">.</span><span class="nf">VerifyUserHasAnyAcceptedScope</span><span class="p">(</span><span class="s">"Contact.View"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I added this to the <code class="language-plaintext highlighter-rouge">GetContacts</code> methods.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">Domain</span><span class="p">.</span><span class="n">Models</span><span class="p">.</span><span class="n">Contact</span><span class="p">&gt;</span> <span class="nf">GetContacts</span><span class="p">()</span>
<span class="p">{</span>
    <span class="n">HttpContext</span><span class="p">.</span><span class="nf">VerifyUserHasAnyAcceptedScope</span><span class="p">(</span><span class="s">"Contact.View"</span><span class="p">);</span>
    <span class="k">return</span> <span class="n">_contactManager</span><span class="p">.</span><span class="nf">GetContacts</span><span class="p">();</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Note, if you do not provide other scopes or don’t add this scope to any other methods, they will no longer be accessible.</p>

<h3 id="configure-postman">Configure Postman</h3>

<p>If you don’t have Postman, you get download it <a href="https://www.postman.com" target="_blank">here</a>. We are using Postman to validate the authentication and authorization of the application.</p>

<p>Add a new request in Postman to get the contacts which will call the <code class="language-plaintext highlighter-rouge">\Contacts</code> end point.</p>

<ul>
  <li>Click the ‘+’ button on the tabs</li>
  <li>Select ‘Get’, if it is not already selected</li>
  <li>Enter the Uri of <code class="language-plaintext highlighter-rouge">https://localhost:5001/Contacts</code> or whatever Uri you are testing</li>
  <li>Click on ‘Authorization’</li>
  <li>For <strong>Type</strong>, select ‘OAuth 2.0’</li>
  <li>Click ‘Get New Access Token’</li>
</ul>

<p><img src="/assets/images/posts/protect-api-postman-access-token.png" alt="Protect an Api - Postman - Get a New Access Token" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Name</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Token Name</td>
      <td><code class="language-plaintext highlighter-rouge">AzureAD</code></td>
      <td>Any name you want</td>
    </tr>
    <tr>
      <td>Grant Type</td>
      <td><code class="language-plaintext highlighter-rouge">Authorization Code</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Callback URL</td>
      <td><code class="language-plaintext highlighter-rouge">https://www.postman.com/oauth2/callback</code></td>
      <td>This is the value you entered when you registered the client in the Azure portal</td>
    </tr>
    <tr>
      <td>Auth URL</td>
      <td><code class="language-plaintext highlighter-rouge">https://login.microsoftonline.com/common/oauth2/v2.0/authorize</code></td>
      <td>You can get this from the ‘End Points’ blade in your Client Registration</td>
    </tr>
    <tr>
      <td>Access Token URL</td>
      <td><code class="language-plaintext highlighter-rouge">https://login.microsoftonline.com/common/oauth2/v2.0/token</code></td>
      <td>You can get this from the ‘End Points’ blade in your Client Registration</td>
    </tr>
    <tr>
      <td>Client ID</td>
      <td><em><code class="language-plaintext highlighter-rouge">clientId</code></em></td>
      <td>the client id for your client app in the Azure portal</td>
    </tr>
    <tr>
      <td>Client Secret</td>
      <td><em><code class="language-plaintext highlighter-rouge">client secret</code></em></td>
      <td>The client secret you created in the Azure portal</td>
    </tr>
    <tr>
      <td>Scope</td>
      <td><code class="language-plaintext highlighter-rouge">api://747cc384-f337-4138-a25b-6b38664404a5/Contact.View</code></td>
      <td>The scope(s) you want a token for</td>
    </tr>
    <tr>
      <td>State</td>
      <td><em>leave blank</em></td>
      <td> </td>
    </tr>
    <tr>
      <td>Client Authentication</td>
      <td> </td>
      <td>This setting doesn’t matter for this scenario</td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Click ‘Request Token’</li>
</ul>

<p>After authenticating with your Microsoft or Domain account, you will be presented with a Permissions screen similar to this.</p>

<p><img src="/assets/images/posts/protect-api-login-permissions.png" alt="Protect an Api - Postman - Permission Concent" class="align-center" /></p>

<p>After you click ‘Yes’ you will be presented with the ‘Manage Access Tokens’ screen in Postman. Click ‘Use Token’.  If you are curious what is in that token, visit <a href="https://jwt.ms" target="_blank">jwt.ms</a> and paste the contents of that token into the textbox.</p>

<p>At this point you should be able to click on ‘Send’.</p>

<h3 id="try-it-out">Try it out</h3>

<p>Make sure the API is running. Once you visit an endpoint you will get an HTTP status code of 401 - Unauthorized.</p>

<ul>
  <li>Click ‘Send’ in Postman</li>
</ul>

<p>If everything was setup correctly, you should see the contacts.</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="nl">"contactId"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w">
        </span><span class="nl">"firstName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Joseph"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"middleName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"James"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"lastName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Guadagno"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"emailAddress"</span><span class="p">:</span><span class="w"> </span><span class="s2">"jguadagno@hotmail.com"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"birthday"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1900-05-31T00:00:00"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"anniversary"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1996-10-26T10:00:00"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"imageUrl"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w">
        </span><span class="nl">"addresses"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"phones"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
        </span><span class="nl">"fullName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Joseph James Guadagno"</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="troubleshooting">Troubleshooting</h4>

<h5 id="401">401</h5>

<p>Good luck!</p>

<p>If you get a ‘error_description’ with it like <code class="language-plaintext highlighter-rouge">Bearer error="invalid_token", error_description="The audience '*some guid*' is invalid"</code>. This means you have the wrong client id in your appsettings.json.</p>

<p>If you don’t get an ‘error_description’ with it, that generally means something is wrong with the application registration. Go over the steps above.</p>

<h5 id="500-internal-error">500 Internal Error</h5>

<p>You should get an error message with this. The most likely cause is that the name of the scope you have in the c# code does not exists in the token.</p>

<h2 id="wrap-up">Wrap up</h2>

<p>This is a rather long post. The good thing about using the Microsoft Identity Platform is once you set up the App Registrations in the Azure portal and do it correctly :smile:, the rest is easy.</p>

<p>After the team at Microsoft watched me struggle through it, they said they would make it easier and probably even have some Powershell scripts for the initial setup.</p>

<h2 id="other-resources">Other Resources</h2>

<p>If you are interesting in learning how to build an ASP.NET Core Web API, you can start with the <a href="https://www.github.com/jguadagno/contacts" target="_blank">Contacts</a> example application. Or you can read along with instructions at this blog post: <a href="https://www.toptal.com/asp-dot-net/asp-net-web-api-tutorial" target="_blank">Building an ASP.NET Web API With ASP.NET Core</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Identity" /><category term="WebAPI" /><category term="MSAL" /><category term="Managed Identity" /><category term="Entra" /><summary type="html"><![CDATA[Microsoft Identity Web is a library which contains a set of reusable classes used in conjunction with ASP.NET Core for integrating with the Microsoft identity platform (formerly Azure AD v2.0 endpoint) and AAD B2C.]]></summary></entry><entry><title type="html">Hello Microsoft Identity Platform</title><link href="https://www.josephguadagno.net/2020/06/12/hello-microsoft-identity-platform" rel="alternate" type="text/html" title="Hello Microsoft Identity Platform" /><published>2020-06-12T00:00:00-07:00</published><updated>2020-06-12T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/06/12/hello-microsoft-identity-platform</id><content type="html" xml:base="https://www.josephguadagno.net/2020/06/12/hello-microsoft-identity-platform"><![CDATA[<p>Hello, <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/?wt.mc_id=DT-MVP-4024623" target="_blank">Microsoft identity platform</a>!</p>

<p>The team at Microsoft has been working on making Authentication and Authorization easier for us engineers. Since I was close to adding Authentication and Authorization to the <a href="https://www.github.com/jguadagno/contacts" target="_blank">Contacts</a> API that I have been working on on ‘<a href="https://twitch.tv/jguadagno" target="_blank">Coding with JoeG</a>’, <a href="https://www.bradygaster.com/">Brady Gaster</a> {:target=”_blank”}reached out and wanted me to try the new package. I agreed, and we planned for me to implement the package on my stream on June 11th.  It did not go well even though we had <a href="https://www.bradygaster.com/" target="_blank">Brady Gaster</a>, <a href="https://cmatskas.com/" target="_blank">Christos Matskas</a>, <a href="https://www.linkedin.com/in/darrelmiller/" target="_blank">Darrel Miller</a>, and others helping. You can watch the episode on <a href="https://youtu.be/xy1t3EaHOdc" target="_blank">YouTube</a> if you wish. Part of the reason it did not go well was that I went down a slightly dated documentation rabbit hole, and part of it was ‘live’ debugging on Twitch, which by the way, had like a 20-30 delay. After 3 hours of trying to figure it out, we called it a night.  However, that did not stop me or the team at Microsoft from figuring out what happened. Both Brady and Christos reached out to me with a solution the following morning that we got working in a few minutes.</p>

<p>I’m going to create a blog post about how we implemented it <a href="/2020/06/12/protecting-an-asp-net-core-api-with-microsoft-identity-platform">here</a>.  We’ll stream the implementation or adding Authentication and Authorization on Monday’s stream.</p>

<p>Thanks, Brady, Christos, Darrel, and the team for the assist!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Identity" /><category term="WebAPI" /><category term="Api" /><category term="OAuth" /><category term="MSAL" /><category term="Managed Identity" /><category term="Entra" /><summary type="html"><![CDATA[Hello, Microsoft identity platform!]]></summary></entry><entry><title type="html">Coding with JoeG - Streaming Software Configuration</title><link href="https://www.josephguadagno.net/2020/05/25/coding-with-joeg-streaming-software-configuration" rel="alternate" type="text/html" title="Coding with JoeG - Streaming Software Configuration" /><published>2020-05-25T04:05:00-07:00</published><updated>2020-12-25T09:30:00-07:00</updated><id>https://www.josephguadagno.net/2020/05/25/coding-with-joeg-streaming-software-configuration</id><content type="html" xml:base="https://www.josephguadagno.net/2020/05/25/coding-with-joeg-streaming-software-configuration"><![CDATA[<p class="notice--info"><strong><em>NOTE</em></strong> Since I have a new hardware configuration as outlined in ‘<a href="/2020/12/20/coding-with-joeg-streaming-hardware-configuration-december-2020-update">Stream Hardware Configuration - December 2020</a>, I’ve updated the software configuration in the post <a href="/2020/12/25/coding-with-joeg-streaming-software-configuration-december-2020-update">Stream Software Configuration - December 2020</a>. Check it out!</p>

<p>In a previous post, I covered the <a href="/2020/05/20/coding-with-joeg-streaming-hardware-configuration">hardware</a> I use for the stream.  In this post, I’ll cover the software and settings to ‘produce’ the stream and artifacts. Please keep in mind, this is a guide based on some of my searchings, my playing around, and my hardware, your needs may be different. Another thing to keep in mind, is these are my settings for streaming live coding not live gaming.</p>

<h2 id="streamlabs-obs-configuration">Streamlabs OBS Configuration</h2>

<p>Streamlabs OBS is a free reliable streaming app with the fastest setup process on the market. We have developed an all-in-one application making streaming easy for everyone. Whether you’re a novice or experienced streamer, Streamlabs OBS will provide you the best streaming experience, with tools built to engage, grow, and monetize your channel.</p>

<p><a href="https://streamlabs.com/streamlabs-obs">Download</a></p>

<h3 id="settings">Settings</h3>

<p>The first thing, from what I remember, is that Streamlabs OBS runs an ‘Auto Optimize’ function, which you can find on the general tab of settings.  From everything I have seen and read, you really never want to do this, except for maybe the first time.  Most of the time, you will want to ‘tweak’ the settings to fit your needs.  I’ve been on streams where the host says “What a minute. I have to tweak something”.  It’s gonna happen for a while.</p>

<p>First we are going to talk about the Streamlabs OBS settings.  You can get to these by clicking on gear (<i class="fas fa-cog"></i>) icon. Again, some settings might not be applicable depending on your hardware.</p>

<h4 id="general-tab">General Tab</h4>

<p>Everything in this tab is according to your preference.  For me, everything is unchecked except for:</p>

<ul>
  <li>Confirm stream title and game before going live</li>
  <li>In the ‘Output’ section, Automatically record when streaming</li>
  <li>In the ‘Source Alignment Snapping’, all 3 checked.</li>
</ul>

<h4 id="stream-tab">Stream Tab</h4>

<p>Enable multistream, if you are.  For me, as the writing of this post, I’m only live streaming to <a href="https://twitch.tv/jguadagno" target="_blank">Twitch</a></p>

<h4 id="output-tab">Output Tab</h4>

<p>For this tab, I have a few things tweaked based on my hardware and conditions.  Please select the <code class="language-plaintext highlighter-rouge">Advanced</code> option for ‘Output mode’.</p>

<h5 id="output---streaming">Output - Streaming</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Audio Track</td>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
      <td>Only have one track.  This is helpful if you want to separate music from your voice</td>
    </tr>
    <tr>
      <td>Encoder</td>
      <td><code class="language-plaintext highlighter-rouge">Hardware (QSV)</code></td>
      <td>This is set to software, by default, I changed it to hardware because I wanted to offload some of the video encoding to my video chip/card since my PC not fast enough</td>
    </tr>
    <tr>
      <td>Enforce stream service encoder settings</td>
      <td><code class="language-plaintext highlighter-rouge">Unchecked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Rescale output</td>
      <td><code class="language-plaintext highlighter-rouge">Unchecked</code></td>
      <td>I broadcast at the resolution I want.</td>
    </tr>
    <tr>
      <td>Target Usage</td>
      <td><code class="language-plaintext highlighter-rouge">quality</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Profile</td>
      <td><code class="language-plaintext highlighter-rouge">high</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Keyframe Interval</td>
      <td><code class="language-plaintext highlighter-rouge">3</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Async Depth</td>
      <td><code class="language-plaintext highlighter-rouge">4</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Rate Control</td>
      <td><code class="language-plaintext highlighter-rouge">VBR</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Bitrate</td>
      <td><code class="language-plaintext highlighter-rouge">2500</code></td>
      <td>The higher the better</td>
    </tr>
    <tr>
      <td>B Frames</td>
      <td>3``</td>
      <td> </td>
    </tr>
    <tr>
      <td>Content Adaptive Quantization</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="output---recording">Output - Recording</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Type</td>
      <td><code class="language-plaintext highlighter-rouge">Standard</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Recording Path</td>
      <td><em>local storage</em></td>
      <td>If you can, this should be a SSD on a different partition/device than your operating system.  Avoid recording to a network drive</td>
    </tr>
    <tr>
      <td>Generate File Name without Space</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Recording Format</td>
      <td><code class="language-plaintext highlighter-rouge">mp4</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Audio Track</td>
      <td><code class="language-plaintext highlighter-rouge">1</code></td>
      <td>Only have one track.  This is helpful if you want to separate music from your voice</td>
    </tr>
    <tr>
      <td>Encoder</td>
      <td><code class="language-plaintext highlighter-rouge">Hardware (QSV)</code></td>
      <td>This is set to software, by default, I changed it to hardware because I wanted to offload some of the video encoding to my video chip/card since my PC not fast enough</td>
    </tr>
    <tr>
      <td>Rescale output</td>
      <td><code class="language-plaintext highlighter-rouge">Unchecked</code></td>
      <td>I broadcast at the resolution I want.</td>
    </tr>
    <tr>
      <td>Custom Mixer Settings</td>
      <td><em>empty</em></td>
      <td> </td>
    </tr>
    <tr>
      <td>Target Usage</td>
      <td><code class="language-plaintext highlighter-rouge">quality</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Profile</td>
      <td><code class="language-plaintext highlighter-rouge">high</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Keyframe Interval</td>
      <td><code class="language-plaintext highlighter-rouge">3</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Async Depth</td>
      <td><code class="language-plaintext highlighter-rouge">4</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Rate Control</td>
      <td><code class="language-plaintext highlighter-rouge">VBR</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Bitrate</td>
      <td><code class="language-plaintext highlighter-rouge">2500</code></td>
      <td>The higher the better</td>
    </tr>
    <tr>
      <td>Max Bitrate</td>
      <td><code class="language-plaintext highlighter-rouge">3000</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>B Frames</td>
      <td>3``</td>
      <td> </td>
    </tr>
    <tr>
      <td>Content Adaptive Quantization</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="output---audio">Output - Audio</h5>

<p>All of these settings are untouched</p>

<h5 id="output---replay-buffer">Output - Replay Buffer</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enabled Replay Buffer</td>
      <td><code class="language-plaintext highlighter-rouge">Checked</code></td>
      <td>This was on by default, I have not changed it</td>
    </tr>
    <tr>
      <td>Maximum Replay Time (Seconds)</td>
      <td><code class="language-plaintext highlighter-rouge">20</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h4 id="audio">Audio</h4>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Sample Rate</td>
      <td><code class="language-plaintext highlighter-rouge">48khz</code></td>
      <td>Set this to the highest value your audio will support! The higher the hertz, the better the audio quality</td>
    </tr>
    <tr>
      <td>Channels</td>
      <td><code class="language-plaintext highlighter-rouge">Stereo</code></td>
      <td>Especially if you have music playing</td>
    </tr>
    <tr>
      <td>Desktop Audio Device 1</td>
      <td><code class="language-plaintext highlighter-rouge">Default</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Desktop Audio Device 2</td>
      <td><code class="language-plaintext highlighter-rouge">Disabled</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Mic/Auxiliary Device 1</td>
      <td><em>Unselected</em></td>
      <td> </td>
    </tr>
    <tr>
      <td>Mic/Auxiliary Device 2</td>
      <td><code class="language-plaintext highlighter-rouge">Disabled</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Mic/Auxiliary Device 3</td>
      <td><code class="language-plaintext highlighter-rouge">Disabled</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>You can select your microphone for either of the <em>mic/auxiliary</em> slots if you chose to. Doing so will make your microphone available to all scenes without adding it.</p>

<h4 id="video">Video</h4>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Base (Canvas) Resolution</td>
      <td><code class="language-plaintext highlighter-rouge">3840x2160</code></td>
      <td><strong>4k</strong>. I have one screen with 4K but I use that for monitoring stuff. I’ll probably change this.</td>
    </tr>
    <tr>
      <td>Output (Scaled) Resolution</td>
      <td><code class="language-plaintext highlighter-rouge">19020x1080</code></td>
      <td><strong>HD</strong></td>
    </tr>
    <tr>
      <td>Downscale Filter</td>
      <td><code class="language-plaintext highlighter-rouge">Bicubic (Sharpened scaling, 16 samples)</code></td>
      <td><strong>Bilateral</strong>, best for CPU. <strong>Lanczos</strong>, is better quality but CPU intensive. <strong>Bicubic</strong>, is the in-between.</td>
    </tr>
    <tr>
      <td>FPS Type</td>
      <td><code class="language-plaintext highlighter-rouge">Common FPS Values</code></td>
      <td>This will defaults to Frames per Second based on the resolution</td>
    </tr>
    <tr>
      <td>Common FPS Values</td>
      <td><code class="language-plaintext highlighter-rouge">60</code></td>
      <td>You can lower this to 30 if you are seeing an issue with your CPU. For streaming code, 30 is an acceptable value.</td>
    </tr>
  </tbody>
</table>

<h4 id="advanced">Advanced</h4>

<h5 id="advanced---general">Advanced - General</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Process Priority</td>
      <td><code class="language-plaintext highlighter-rouge">High</code></td>
      <td>I have this set to high because I am using a separate machine for streaming otherwise you should probably chose <code class="language-plaintext highlighter-rouge">normal</code></td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---video">Advanced - Video</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Color Format</td>
      <td><code class="language-plaintext highlighter-rouge">NV12</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>YUV Color Space</td>
      <td><code class="language-plaintext highlighter-rouge">601</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>YUV Color Range</td>
      <td><code class="language-plaintext highlighter-rouge">Partial</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Force CPU as render device</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td>I wanted to offload the encoding of video to the video chip</td>
    </tr>
  </tbody>
</table>

<p>Some of your default settings for video might be different depending on your locale.</p>

<h5 id="advanced---audio">Advanced - Audio</h5>

<p>I do not have a separate device to ‘monitor’ the stream audio</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Audio Monitoring Device</td>
      <td><code class="language-plaintext highlighter-rouge">Default</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Disable Windows audio ducking</td>
      <td><code class="language-plaintext highlighter-rouge">unchecked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---recording">Advanced - Recording</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Filename Formatting</td>
      <td><code class="language-plaintext highlighter-rouge">%CCYY-%MM-%DD %hh-%mm-%ss</code></td>
      <td>This is the default value. I kept it because I keep all of the recordings… so far.</td>
    </tr>
    <tr>
      <td>Overwrite if file exists</td>
      <td><code class="language-plaintext highlighter-rouge">unchecked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---replay-buffer">Advanced - Replay Buffer</h5>

<p>These are the default values.  I imagine that if you are streaming your gaming, you might want to provide some replay functionality for a ‘sweet kill’.  No one said ever, ‘That was a great use of a lambda, let me replay that’ :smile:</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Replay Buffer Filename Prefix</td>
      <td><code class="language-plaintext highlighter-rouge">Replay</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Replay Buffer Filename Suffix</td>
      <td><em>empty</em></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---stream-delay">Advanced - Stream Delay</h5>

<p>These are the default values.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enable</td>
      <td><code class="language-plaintext highlighter-rouge">Unchecked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Duration</td>
      <td><code class="language-plaintext highlighter-rouge">20</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Preserve cutoff point (increase delay) when reconnection</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---automatically-reconnect">Advanced - Automatically Reconnect</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enable</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Retry Delay (seconds)</td>
      <td><code class="language-plaintext highlighter-rouge">10</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Maximum Retries</td>
      <td><code class="language-plaintext highlighter-rouge">20</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---network">Advanced - Network</h5>

<p>If you have more than one Network Interface Card (nic), exampled wired and wireless. You can set Streamlabs OBS to only use one.  If you have a wired connection, you should use that.  Wireless can drop packets.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Bind to IP</td>
      <td><code class="language-plaintext highlighter-rouge">Default</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Dynamically change bitrate when dropping frames while streaming</td>
      <td><code class="language-plaintext highlighter-rouge">unchecked</code></td>
      <td>If you notice your CPU or internet connection can not handle the load you may want this checked.  Doing so, will drop the frame rate , the number of ‘snapshots’ that the view sees, which could degrade quality.  If you are later posting the recording, I would keep this unchecked.</td>
    </tr>
    <tr>
      <td>Enable new networking code</td>
      <td><code class="language-plaintext highlighter-rouge">unchecked</code></td>
      <td><a href="https://www.reddit.com/r/Twitch/comments/8kwue6/obs_new_network_code_option/">Explanation</a></td>
    </tr>
    <tr>
      <td>Low latency mode</td>
      <td><code class="language-plaintext highlighter-rouge">Unchecked</code></td>
      <td>This should be checked if you are seeing a lot of dropped frames or low/show upload bandwidth from your ISP.</td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---sources">Advanced - Sources</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enable Browser Source Hardware Acceleration</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="advanced---media-files">Advanced - Media Files</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Enable media file caching</td>
      <td><code class="language-plaintext highlighter-rouge">checked</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h4 id="other-settings">Other settings</h4>

<p>All of the other settings, Hotkeys, Game Overlay, Scene Collections, Notifications, Appearance, and Face Masks, I have left the default values.</p>

<h3 id="scenes">Scenes</h3>

<p>Scenes in OBS provide a different way to provide content to the viewer. Do you just want to show code? Do you just want to show you?  Scenes are a way to do that, and a preference.  From what I have seen, no two streams are the same.  I have eight scenes registered, three of them are just helper scenes that are reused as part of other scenes.</p>

<p><img src="/assets/images/posts/streaming-software-scenes.jpg" alt="Coding with JoeG Scenes" class="align-center" /></p>

<table>
  <tbody>
    <tr>
      <td>Scene Name</td>
      <td>Use</td>
    </tr>
    <tr>
      <td>Overlay</td>
      <td>Samples, provided by the theme, with some visual UI elements</td>
    </tr>
    <tr>
      <td>Starting Scene</td>
      <td>I use this when I go live, to let the viewers know “I’ll be there in a minute”</td>
    </tr>
    <tr>
      <td>Live Share - Elgato</td>
      <td>This is my primary scene. It shares the primary capture from the Elgato card with the video from the webcam</td>
    </tr>
    <tr>
      <td>Be Right Back Scene</td>
      <td>I have not used this one yet.  It’s for the times, I might need to run and get the door, or another drink, or something</td>
    </tr>
    <tr>
      <td>Ending Scene</td>
      <td>This signals the users that the stream is ending and provides some social media links for them to continue the conversation.  My mic is still on at this point.  This might be replaced with the <em>Just Chatting</em> scene which I am still building.</td>
    </tr>
    <tr>
      <td>Just Chatting</td>
      <td>Work in progress.  This will just be me with some background images for just the conversation.</td>
    </tr>
    <tr>
      <td>Social Media</td>
      <td>A shared scene with all of my social media links</td>
    </tr>
    <tr>
      <td>Alerts</td>
      <td>A shared scene with the OBS/Twitch alerts for new followers, subscribers, etc.</td>
    </tr>
  </tbody>
</table>

<p>All of my scenes and alerts use the <a href="https://www.own3d.tv/product/pure-series-package/" target="_blank">Pure</a> theme by <a href="https://www.own3d.tv" target="_blank">Own3d</a> for a consistent look.</p>

<h3 id="filters">Filters</h3>

<p>Filters are like advanced settings that let you tweak your devices (sources) even more.  I’m going to cover some of the Webcam and Microphone filters I use to provide a better experience.</p>

<p>One thing to keep in mind is that the order of the filters matter.  The output of one filter is feed in as the source of the next filter.  So if you have four filters, the input of filter 4 would be the output of the third, not the original source data. So, you might want to play around with the ordering a bit.</p>

<p>To get to the filters in OBS, right-click on a source and chose <code class="language-plaintext highlighter-rouge">Filters</code>.</p>

<h4 id="webcam-filters">Webcam Filters</h4>

<h5 id="webcam-filter---croppad">Webcam Filter - Crop/Pad</h5>

<p>This filter will remove or add pixels to your webcam.  The webcam I use has a wide range, so I crop out a lot of it just so my face and shoulders are available.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Left</td>
      <td><code class="language-plaintext highlighter-rouge">300</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Top</td>
      <td><code class="language-plaintext highlighter-rouge">180</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Right</td>
      <td><code class="language-plaintext highlighter-rouge">300</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Bottom</td>
      <td><code class="language-plaintext highlighter-rouge">50</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="webcam-filter---sharpen">Webcam Filter - Sharpen</h5>

<p>Next up is the <em>Sharpen</em> filter. This is used to clean up the image a little bit.  For my camera, this is barely needed.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Sharpness</td>
      <td><code class="language-plaintext highlighter-rouge">0.08</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="webcam-filter---chroma-key">Webcam Filter - Chroma Key</h5>

<p>Chromakeying is used to mask out the background of your camera’s field of view.  Think of the weather forecaster on TV standing in front of the weather map.  That image is superimposed by a computer by replacing the Chromakeying color with the computer image.  I have a Green chromakey for my setup.</p>

<p>You will probably mess around with these settings a lot until you get it just right.  The lighting in your room/office/studio is a big contributor to this.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Key Color Type</td>
      <td><code class="language-plaintext highlighter-rouge">Green</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Similarity</td>
      <td><code class="language-plaintext highlighter-rouge">440</code></td>
      <td><strong>Note</strong>: I play around with this number a lot depending on the lighting in my room.  I usually don’t go under <code class="language-plaintext highlighter-rouge">400</code> or higher than <code class="language-plaintext highlighter-rouge">450</code></td>
    </tr>
    <tr>
      <td>Smoothness</td>
      <td><code class="language-plaintext highlighter-rouge">80</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Key Color Spill Reduction</td>
      <td><code class="language-plaintext highlighter-rouge">100</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Opacity</td>
      <td><code class="language-plaintext highlighter-rouge">100</code></td>
      <td>Your personal preference. How much of yourself do you want on the screen?</td>
    </tr>
    <tr>
      <td>Contrast</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Brightness</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Gamma</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p><strong>Semi-pro</strong> tip: Don’t wear the same color shirt as your Chromakey :smile:</p>

<h4 id="microphone-filters">Microphone Filters</h4>

<p>These filters were based off of a video I watched <a href="https://www.youtube.com/watch?v=JvFw1NsgElQ">Best Microphone Settings for Streamlabs OBS (2020)</a>. I recommend you watch this video to learn more about the audio filters.</p>

<h5 id="microphone-filter---gain">Microphone Filter - Gain</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Gain</td>
      <td><code class="language-plaintext highlighter-rouge">13.4</code></td>
      <td>The more gain, the louder the audio is</td>
    </tr>
  </tbody>
</table>

<h5 id="microphone-filter---noise-suppression">Microphone Filter - Noise Suppression</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Suppression Level</td>
      <td><code class="language-plaintext highlighter-rouge">-30</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="microphone-filter---noise-gate">Microphone Filter - Noise Gate</h5>

<p>These settings will require a lot of tweaking to get a clear sound for your recording. I find myself tweaking the Close and Open Thresholds mostly.  The video above does a great job in explaining each of the settings.</p>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Close Threshold</td>
      <td><code class="language-plaintext highlighter-rouge">-51</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Open Threshold</td>
      <td><code class="language-plaintext highlighter-rouge">-26</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Attack Time</td>
      <td><code class="language-plaintext highlighter-rouge">25</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Hold Time</td>
      <td><code class="language-plaintext highlighter-rouge">200</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Release Time</td>
      <td><code class="language-plaintext highlighter-rouge">150</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h5 id="microphone-filter---compressor">Microphone Filter - Compressor</h5>

<table>
  <tbody>
    <tr>
      <td>Setting</td>
      <td>Value</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Ratio</td>
      <td><code class="language-plaintext highlighter-rouge">10</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Threshold</td>
      <td><code class="language-plaintext highlighter-rouge">-18</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Attack</td>
      <td><code class="language-plaintext highlighter-rouge">6</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Release</td>
      <td><code class="language-plaintext highlighter-rouge">60</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Output Gain</td>
      <td><code class="language-plaintext highlighter-rouge">0</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Sidechain/Ducking Source</td>
      <td><code class="language-plaintext highlighter-rouge">None</code></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h2 id="wrap-up">Wrap Up</h2>

<p>That’s it!  Again, your mileage may vary.  These are the ‘optimal’ settings for my hardware, software, and environment.</p>

<p>For details on my hardware, check out this <a href="/2020/05/20/coding-with-joeg-streaming-hardware-configuration">post</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Twitch" /><category term="Stream" /><category term="CodingWithJoeG" /><category term="Archive" /><summary type="html"><![CDATA[NOTE Since I have a new hardware configuration as outlined in ‘Stream Hardware Configuration - December 2020, I’ve updated the software configuration in the post Stream Software Configuration - December 2020. Check it out!]]></summary></entry><entry><title type="html">Branding Your Stream</title><link href="https://www.josephguadagno.net/2020/05/20/branding-your-stream" rel="alternate" type="text/html" title="Branding Your Stream" /><published>2020-05-20T04:05:00-07:00</published><updated>2020-12-20T08:30:00-07:00</updated><id>https://www.josephguadagno.net/2020/05/20/branding-your-stream</id><content type="html" xml:base="https://www.josephguadagno.net/2020/05/20/branding-your-stream"><![CDATA[<p>As you start streaming more or recording videos for the future, there are few things you may want to consider to <em>tidy</em> things up.  Here are a few things you should consider.</p>

<h2 id="branding">Branding</h2>

<p>Building a brand is essential. Many speakers, presenters, and streamers have a brand. Scott Gu has his red polo shirt, and now dinosaur slippers. Ward Bell has ‘<em>crazy</em>’ outfits that he wears. Dan Wahlin wears a polo shirt in the shade of blue. I wear a polo shirt, with my company logo on it along with khakis. Keeping that consistent brand is key to people remembering. When they see you out of that element, they almost don’t recognize you :smile:. The same goes for your stream, YouTube, and social media sites. The look, font, images, and colors should be consistent to tie your brand together. Please take a look at my <a href="https://www.twitch.tv/jguadagno" target="_blank">Twitch</a> page and my <a href="https://jjg.me/youtube" target="_blank">YouTube channel</a>.  The artwork is consistent across.  All of the newer videos are using the same images. Another great example is Brian Lagunas’s, (<a href="https://twitter.com/brianlagunas" target="_blank">@brianlagunas</a>) <a href="https://www.twitch.tv/brianlagunas" target="_blank">Twitch</a> stream and his <a href="https://www.youtube.com/brianlagunas" target="_blank">YouTube</a> channel. There is consistency through-out his channels.</p>

<p>Now, you don’t need to be a designer or have a designer.  There are many sites out there to help you.  You can quickly start with a theme.</p>

<h2 id="themes">Themes</h2>

<p>If you are using Streamlabs OBS or OBS itself, they support themes.  Themes are an excellent, semi-easy way to keep a consistent look across your stream, Twitch, and YouTube.  These sites provide some free and paid themes.</p>

<ul>
  <li><a href="https://streamlabs.com/themes" target="_blank">Streamlabs</a></li>
  <li><a href="https://placeit.net/twitch-templates" target="_blank">Placeit</a></li>
  <li><a href="https://www.own3d.tv/shop/twitch-stream-overlays-templates/" target="_blank">Own3d</a></li>
</ul>

<p>The theme I use is <a href="https://www.own3d.tv/product/pure-series-package/" target="_blank">Pure</a> by <a href="https://www.own3d.tv" target="_blank">Own3d</a>.</p>

<h2 id="images">Images</h2>

<p>Here are some images that you might want to create for your stream.  Recommended image sizes are in parenthesis’.</p>

<ul>
  <li>Stream Logo</li>
  <li>Twitch
    <ul>
      <li>Offline Page (<em>1920x1080</em>)</li>
      <li>Channel Header/Profile Banner (<em>1200x380</em>)</li>
      <li>Stream Introduction Image (<em>1920x1080</em>)</li>
      <li>Stream Ending (“Outro”) Image (<em>1920x1080</em>)</li>
    </ul>
  </li>
  <li>YouTube <a href="https://www.veed.io/learn/youtube-banner-size">Veed.io post</a>
    <ul>
      <li>Intro Image (<em>1920x1080</em>)</li>
      <li>End Image (<em>1920x1080</em>)</li>
      <li>Thumbnail (<em>1280x720</em>)</li>
      <li>Channel Art (<em>2560x1440</em>) <a href="https://support.google.com/youtube/answer/2972003?visit_id=637255225946398540-2674606628&amp;p=picker_channel_art&amp;hl=en&amp;rd=1" target="_blank">Details</a></li>
    </ul>
  </li>
</ul>

<p>Happy Streaming!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term="Twitch" /><category term="Stream" /><category term="CodingWithJoeG" /><category term="Archive" /><summary type="html"><![CDATA[As you start streaming more or recording videos for the future, there are few things you may want to consider to tidy things up. Here are a few things you should consider.]]></summary></entry><entry><title type="html">Coding with JoeG - Streaming Hardware Configuration</title><link href="https://www.josephguadagno.net/2020/05/20/coding-with-joeg-streaming-hardware-configuration" rel="alternate" type="text/html" title="Coding with JoeG - Streaming Hardware Configuration" /><published>2020-05-20T04:05:00-07:00</published><updated>2020-12-20T09:30:00-07:00</updated><id>https://www.josephguadagno.net/2020/05/20/coding-with-joeg-streaming-hardware-configuration</id><content type="html" xml:base="https://www.josephguadagno.net/2020/05/20/coding-with-joeg-streaming-hardware-configuration"><![CDATA[<p class="notice--info"><strong><em>NOTE</em></strong> I have a new hardware configuration as outlined in ‘<a href="/2020/12/20/coding-with-joeg-streaming-hardware-configuration-december-2020-update">Stream Hardware Configuration - December 2020</a>. Check it out!</p>

<p>I’ve listed out all of the equipment that I use to stream ‘Coding with JoeG’ on <a href="https://twitch.tv/jguadagno" target="_blank">Twitch</a>. While all this equipment is not necessary, it has helped me produce a reliable stream.  <em>Your mileage may vary!</em></p>

<h2 id="computer-equipment">Computer Equipment</h2>

<h3 id="primary-pc">Primary PC</h3>

<p>This is the laptop that I do my presentations from. A <strong>Mac Book Pro 2018 - 15 inch</strong> with the specs of:</p>

<ul>
  <li>Intel i7 6 Core</li>
  <li>16 Gb of memory</li>
  <li>512 Gb SSD</li>
  <li>Graphics
    <ul>
      <li>Radeon Pro 560x</li>
      <li>Intel UHD graphics 630</li>
    </ul>
  </li>
</ul>

<h3 id="secondary-pc">Secondary PC</h3>

<p>I use a secondary PC to manage the stream since my primary pc was not strong enough to simultaneously handle the coding and streaming. For that, I use a <strong>Lenovo Flex 5-1570</strong> with the following specs:</p>

<p>View on <a href="https://www.lenovo.com/us/en/laptops/ideapad/ideapad-flex-series/IdeaPad-Flex-5-15IIL-05/p/88IPF501454" target="_blank">Manufacturers Site</a></p>

<ul>
  <li>Intel i7-08550U 4 Core (8 logical processors)</li>
  <li>16 gig of memory</li>
  <li>512 gig SSD</li>
  <li>Graphics
    <ul>
      <li>Intel UHD Graphics 620</li>
      <li>NVIDIA GeForce MX130</li>
    </ul>
  </li>
</ul>

<h2 id="streaming-gear">Streaming ‘Gear’</h2>

<h3 id="each-webcam-1080p">“Each” WebCam 1080p</h3>

<p>View on <a href="https://amzn.to/3e4kS3I" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-each-webcam.jpg" alt="Each Web Cam" class="align-center" /></p>

<p>While I could have used the built-in webcam on either laptop for the stream, the ‘Each’ webcam allows me to customize the webcam settings further. Luckily, with this camera, I don’t need to customize anything. The default, “out of the box”, settings produce an excellent quality 1080p image.  Another good thing about this camera is that I can mount on top of the laptop or tripod.</p>

<h3 id="audio-technica-atr2100x-usb-usbxlr-microphone-bundle-with-knox-gear-boom-arm-shock-mount-and-pop-filter">Audio-Technica ATR2100X-USB USB/XLR Microphone Bundle with Knox Gear Boom Arm, Shock Mount, and Pop Filter</h3>

<p>View on <a href="https://www.audio-technica.com/cms/wired_mics/b8dd84773f83092c/index.html" target="_blank">Manufacturers Site</a>
View on <a href="https://amzn.to/2XiLx5Q" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-atr2100x.jpg" alt="Audio-Technica ATR2100X-USB USB/XLR Microphone" class="align-center" /></p>

<p>A microphone is probably the one piece of hardware that I recommend you buy, not necessarily the ATR2100. I love the sound quality of this microphone so far.  Most built-in microphones are low quality and, on laptops, are next to the keyboard.  These two factors lead to the microphone picking up considerable extra noise.  Having a separate microphone mounted on a ‘Boom arm’ with the shock mount and pop filter reduces much noise.  With this configuration, you can’t hear me type, the fan in my office, or when I accidentally bang the desk.  This microphone is a great starter purchase.  As a bonus, this comes with an XLR connection to plug into a mixer board and other gear if you want to get serious about your audio.</p>

<h3 id="elgato-hd-60-s">Elgato HD 60 S+</h3>

<p>View on <a href="https://www.elgato.com/en/gaming/game-capture-hd60-s-plus" target="_blank">Manufacturer Site</a>
View on <a href="https://amzn.to/2WOiVTi" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-hd60.jpg" alt="Elgato Game Capture HD60 S+" class="align-center" /></p>

<p>Don’t let the name ‘Game Capture’ fool you. This device does more than capture game footage.  I think the initial thought behind the product was for game capture. However, this device allows you to capture audio and video via HDMI and use it as a Video Source in OBS. <a href="https://howto.streamlabs.com/streamlabs-obs-19/elgato-capture-card-setup-in-streamlabs-obs-23" target="_blank">Setup</a> is fairly easy and everything you need is included.</p>

<h3 id="green-screening--chroma-keying">Green Screening / Chroma Keying</h3>

<p>“Green Screening” or chroma keying allows software like OBS to easily separate green screens and panels from the people standing in front of them and replace those backgrounds with pretty much anything.  You’ve seen chroma keying in Hollywood special effects when superheroes fly or while watching your local weather forecast when the weather person is magically standing in front of a map.  Many streamers use chroma keying to drop out everything behind them, so you only see them on the stream.  I use the ePhotoInc chroma key collapsible blue/green background with the Fovitec stand to hold it.</p>

<p>For OBS to remove or replace the background, you need to add a <code class="language-plaintext highlighter-rouge">Chromakey</code>.  You can check out the post ‘<a href="/2020/05/25/coding-with-joeg-streaming-software-configuration">Coding with JoeG - Software Configuration</a>’ for details on my configuration.</p>

<h4 id="ephotoinc-5-x-7-photo-collapsible-chromakey-green-blue-background-2-in-1-backdrop-panel-t57bg">ePhotoInc 5’ x 7’ Photo Collapsible CHROMAKEY Green Blue Background 2 in 1 Backdrop Panel T57BG</h4>

<p>View on <a href="https://amzn.to/2LJnoA3" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-chromakey.jpg" alt="ePhotoInc 5' x 7' Photo Collapsible CHROMAKEY Green Blue Background" class="align-center" /></p>

<h4 id="fovitec---76-clip-stand-kit-for-photo--video-pop-out-backdrops--reflectors">Fovitec - 7’6” Clip Stand Kit for Photo &amp; Video Pop-Out Backdrops &amp; Reflectors</h4>

<p>View on <a href="https://www.fovitec.com/products/76-clip-stand-kit-for-photo-video-pop-out-backdrops-reflectors" target="_blank">Manufacturers Site</a>
View on <a href="https://amzn.to/36fh0tX" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-stand-kit.jpg" alt="Fovitec - 7 foot 6 inch Clip Stand Kit for Photo &amp; Video Pop-Out Backdrops &amp; Reflectors" class="align-center" /></p>

<h2 id="helpful-extras">Helpful Extras</h2>

<h3 id="elgato-streamdeck">Elgato StreamDeck</h3>

<p>View on <a href="https://www.elgato.com/en/gaming/stream-deck" target="_blank">Manufacturers Site</a>
View on <a href="https://amzn.to/2XbYyON" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-streamdeck.jpg" alt="Elgato StreamDeck" class="align-center" /></p>

<p>The StreamDeck provides physical buttons that you can assign to an assortment of things.  You can program scene transitions, sound effects, send tweets, and more.  For me, I have it currently starting a few different applications and sending tweets.</p>

<p>I haven’t used the Elgato StreamDeck to the fullest extend yet.  I’ll play around with it some more this weekend.</p>

<h3 id="desk-lights">Desk Lights</h3>

<p>View on <a href="https://amzn.to/2TnLTHv" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-desk-light.jpg" alt="USB Powered Desk Lamp" class="align-center" /></p>

<p>I have 6 ‘Daylight’ bulbs in my home office, so the lighting is pretty good.  I use these lights mostly to highlight imperfections with shadows (mainly on my face).</p>

<h3 id="amazon-usb-30-hub">Amazon USB 3.0 Hub</h3>

<p>View on <a href="https://amzn.to/3cNRw9B" target="_blank">Amazon</a></p>

<p><img src="/assets/images/posts/streaming-equipment-amazon-usb-hub.jpg" alt="Amazon USB 3.0 Hub" class="align-center" /></p>

<p>A USB hub is not required.  However, because of my current configuration of laptops, I do not have enough USB ports for the microphone, webcam, Elgato card, and Stream Deck.</p>

<p class="notice--info"><strong>Note</strong>: This item is no longer available.</p>

<h3 id="wired-ethernet-connection">Wired Ethernet Connection</h3>

<p>While not required, it is strongly recommended depending on your network configuration.  Both laptops are hard-wired into my router (it’s super close to my desk).  Having both laptops hard-wired prevents wi-fi interference and potential packet/frame dropping.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>Your needs may vary.  This equipment list is what I purchased to deliver an inexpensive quality stream.  Depending on your hardware, you might not need anything from this list. I curated a list of the equipment for the Coding with JoeG Stream at this <a href="https://amzn.to/2ToLEvF" target="_blank">list</a> on Amazon.  <a href="https://www.twitch.tv/csharpfritz" target="_blank">Jeffrey Fritz</a>, part of the <a href="https://www.twitch.tv/team/livecoders" target="_blank">LiveCoders</a> team, also blogged about his configuration, in the <a href="https://jeffreyfritz.com/2019/01/live-streaming-setup-2019-edition/" target="_blank">Live Streaming Setup - 2019 Edition</a></p>

<p class="notice"><strong>Note</strong> If you click on an Amazon link and purchase a product, I may get a commission from Amazon. The purpose of the links is to avoid much searching and not to make money on the blog post.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Twitch" /><category term="Stream" /><category term="CodingWithJoeG" /><category term="Archive" /><summary type="html"><![CDATA[NOTE I have a new hardware configuration as outlined in ‘Stream Hardware Configuration - December 2020. Check it out!]]></summary></entry><entry><title type="html">Getting Started Streaming</title><link href="https://www.josephguadagno.net/2020/05/20/getting-started-streaming" rel="alternate" type="text/html" title="Getting Started Streaming" /><published>2020-05-20T04:05:00-07:00</published><updated>2020-12-20T08:30:00-07:00</updated><id>https://www.josephguadagno.net/2020/05/20/getting-started-streaming</id><content type="html" xml:base="https://www.josephguadagno.net/2020/05/20/getting-started-streaming"><![CDATA[<p>As I <a href="/2020/04/25/coding-with-joeg-im-streaming">posted</a> about a month ago, I starting live coding sessions, aka streaming, on <a href="https://twitch.tv/jguadagno">Twitch</a>. This post is a ‘<em>getting started guide</em>’ for streaming, with a couple of tips and tricks that I have learned in the last month.</p>

<h2 id="getting-started">Getting Started</h2>

<p><em>Just Do It</em>! Don’t overthink it as I did.  Don’t be concerned that there are <em>so many people</em> streaming now. While there may be many people streaming (there isn’t), each person has his or her voice and style. Don’t worry if you don’t know every, no one does.  I’ve watched quite a few people stream before I leaped, and the one thing in common is we all make mistakes, and we all need to look things up.  I plan on demonstrating basic application creation and the way I like to do stuff for my stream.  Some of the stuff I don’t know how to do. Like we say at work, ‘We’ll figure it out’! That is was one of the hardest things for me to get.  I’ve been a public speaker/presenter for over 10 years now, and I was treating each stream like a presentation.  I had to have my scripts, backups of the code, and the plan.  However, streaming is not that.  At least not for me and others.  Streaming is a way to show others how you solve a problem or riddle.  A way to show yourself and others that speakers/presenters are not all-mighty and know everything. So do it!</p>

<h2 id="im-convinced">I’m Convinced</h2>

<p>“<em>You convinced me, Joe! What do I need to do now?</em>” I’m glad you ask! First off, you need to avoid G.A.S., <strong>G</strong>ear <strong>A</strong>cquisition <strong>S</strong>yndrome. G.A.S. is something familiar in the podcasting world and the streaming world to some extent.  The wanting to buy the latest and greatest set of <em>xyz</em>. To get started, you don’t need high-end video cards, cameras, lighting, microphones, or lighting. Most modern computers are enough to handle the basics of what you need.  The shortlist is:</p>

<ul>
  <li>Web Cam, built-in is usually good enough.</li>
  <li>Microphone, built-in is usually good enough.</li>
  <li>Software to stream</li>
</ul>

<h3 id="software-to-stream">Software to Stream</h3>

<p>There are several different ways to stream to Twitch.  Twitch supposed in-browser streaming, which I haven’t tried.  If you want to install software to do it, there are a variety of paid and free software listed here.  Note, these links are provided based on what I have seen.  I have no affiliation with any of these companies :smile:</p>

<ul>
  <li><strong>Open Broadcast Software (OBS)</strong> - Free and open source software for recording and live streaming. Source code is available to everyone to contribute and improve. <a href="https://obsproject.com/">link</a></li>
  <li><strong>Twitch Studio (Beta)</strong> - Our free streaming software, designed to help new streamers get started. With features like guided setup, customizable templates, and integrated alerts, Twitch Studio takes the guesswork out of setting up and managing your stream. <a href="https://www.twitch.tv/studio">link</a></li>
  <li><strong>Streamlabs OBS</strong> - <strong>This is what I use</strong> - Streamlabs OBS is free, open-source, and easy way to get started streaming. The app is built on top of OBS with a whole lot of exciting features: facemasks, built-in widgets &amp; chat, game-specific encoders and thousands of free overlays and themes. <a href="https://streamlabs.com/streamlabs-obs/">link</a></li>
  <li><strong>Lightstream Studio</strong> - Professional live streaming from your browser. Build your ideal layout. Bring on guests remotely. Advanced creative possibilities made easy for anyone. <a href="https://app.golightstream.com/twitchmusic">link</a></li>
  <li><strong>OBS.Live</strong> - OBS.Live is StreamElements’ OBS Studio free add-on for streaming (supports V23). OBS.Live makes stream management easy, integrating Twitch chat, your Activity Feed, and Media Request directly into OBS. Other features include Custom Bot name, HotKeys and Leaderboards. <a href="https://streamelements.com/obslive">link</a></li>
  <li><strong>XSplit Broadcaster</strong> - XSplit is a free, highly customizable, and powerful suite of applications to broadcast to Twitch. Whether creating a live stream or editing/uploading clips, it is easy to get started. <a href="https://www.xsplit.com/partners/twitch">link</a></li>
  <li><strong>Player.me</strong> - Free, easy-to-use, all-in-one application for streaming and recording. Includes thousands of professionally designed overlays with alerts, a stream dashboard for tracking events, live stream chat and scene switching. <a href="https://player.me/">link</a></li>
</ul>

<h3 id="other-considerations">Other Considerations</h3>

<p>While you are streaming, there are a couple of additional things that you need to consider.  If you are using one machine for coding and streaming, it should be able to handle the load. While you are streaming, the software is doing a couple of things at the same time.</p>

<ul>
  <li>Capturing your screen</li>
  <li>Capturing your audio</li>
  <li>Capturing your microphone input</li>
  <li>Encoding this into a format that Twitch or YouTube can handle</li>
  <li>Uploading the content</li>
  <li>Recording it to your hard drive</li>
</ul>

<p>And this is just for the stream, and this does not include whatever else your P.C. is running for the coding you are doing. It would help if you kept this in mind.  During my first couple of streams, I was demonstrating Visual Studio, Visual Studio Code, and Jetbrains Rider, all from the same machine, at the same time. Oh, and I was doing it on a Mac, which meant Visual Studio was running through a Virtual Machine like Parallels.</p>

<p>So lesson learned, keep the running software to a minimum to avoid excess noise from laptop fans and task switching.</p>

<h2 id="ready">Ready</h2>

<p>Now you are ready to stream, go for it.  Here is a quick <a href="https://ardalis.com/streaming-checklist" target="_blank">checklist</a> of things to do just before you start your session or recording. Thanks to Steve, aka <a href="https://ardalis.com/author/ssmith" target="_blank">Ardalis</a>, for the helpful lists.</p>

<h2 id="up-next">Up Next</h2>

<ul>
  <li><a href="/2020/05/20/branding-your-stream">Branding your stream</a></li>
  <li>Coding with JoeG - <del><a href="/2020/05/20/coding-with-joeg-streaming-hardware-configuration">Hardware Configuration</a></del></li>
  <li>Coding with JoeG - <a href="/2020/12/20/coding-with-joeg-streaming-hardware-configuration-december-2020-update">Hardware Configuration - December 2020 Update</a></li>
  <li>Coding with JoeG - <a href="/2020/05/25/coding-with-joeg-streaming-software-configuration">Software Configuration</a></li>
  <li>Coding with JoeG - <a href="/2020/05/20/coding-with-joeg-stream-plugins">OBS Plugins</a> to add to your stream</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Twitch" /><category term="Stream" /><category term="CodingWithJoeG" /><category term="Archive" /><summary type="html"><![CDATA[As I posted about a month ago, I starting live coding sessions, aka streaming, on Twitch. This post is a ‘getting started guide’ for streaming, with a couple of tips and tricks that I have learned in the last month.]]></summary></entry><entry><title type="html">Coding with JoeG - Stream Plugins</title><link href="https://www.josephguadagno.net/2020/05/20/coding-with-joeg-stream-plugins" rel="alternate" type="text/html" title="Coding with JoeG - Stream Plugins" /><published>2020-05-20T00:00:00-07:00</published><updated>2020-05-20T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/05/20/coding-with-joeg-stream-plugins</id><content type="html" xml:base="https://www.josephguadagno.net/2020/05/20/coding-with-joeg-stream-plugins"><![CDATA[<p>Plugins are an easy (in most cases) way to add additional functionality to your stream.  Here is a collection of plugins that I use or are considering on using</p>

<h2 id="plugins-i-use">Plugins I use</h2>

<p>These are plugins that I currently use.</p>

<p><em>Empty</em>, I’m still researching plugins</p>

<h2 id="plugins-to-research">Plugins to Research</h2>

<p>These are plugins for OBS that I want to research a little more to potentially add to my stream</p>

<ul>
  <li><a href="https://obsproject.com/forum/resources/move-transition.913/" target="_blank">Move Transition</a> - This provides a smooth transition between objects in your scenes</li>
  <li><a href="https://obsproject.com/forum/resources/spectralizer.861/" target="_blank">Spectralizer</a> - A visualization of the music you are playing.  Think of it as an old school equalizer :smile:</li>
  <li><a href="https://obsproject.com/forum/resources/obs-shaderfilter-v1-0-update.775/" target="_blank">Shader Filter</a> - This plugin provides a shader that can mask the stream, think of frost on a window in the winter.</li>
  <li><a href="https://obsproject.com/forum/resources/closed-captioning-via-google-speech-recognition.833/" target="_blank">Closed Captioning</a> - This plugin uses the Google speech api in real time to provide real time closed captioning for your stream.</li>
  <li><a href="https://support.streamlabs.com/hc/en-us/articles/360002835654-NDI-Setup-in-Streamlabs-OBS" target="_blank">NDI Setup for Skype</a> - This is how you can use Skype within OBS to allow for guest streams.  It captures the Skype video as a source in OBS.</li>
</ul>

<h2 id="suggestions">Suggestions</h2>

<p>Have any suggestions for plugins for me to give a shot, shoot me an email.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Twitch" /><category term="Stream" /><category term="CodingWithJoeG" /><category term="Archive" /><summary type="html"><![CDATA[Plugins are an easy (in most cases) way to add additional functionality to your stream. Here is a collection of plugins that I use or are considering on using]]></summary></entry><entry><title type="html">What It Means to be a Software Engineer</title><link href="https://www.josephguadagno.net/2020/05/13/what-it-means-to-be-a-software-engineer" rel="alternate" type="text/html" title="What It Means to be a Software Engineer" /><published>2020-05-13T00:00:00-07:00</published><updated>2020-05-13T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/05/13/what-it-means-to-be-a-software-engineer</id><content type="html" xml:base="https://www.josephguadagno.net/2020/05/13/what-it-means-to-be-a-software-engineer"><![CDATA[<p>There has been much talk on social media about being a Software Engineer/Developer/Programmer in the last week or so, probably because we are all being safe and staying in our houses with not much else to do. Conversations have included what it takes to be an engineer, how to get started in engineering, and explaining what a ‘Software Engineer’ does.</p>

<p>Much of the discussions happen because most people do not understand what a ‘Software Engineer’ does, as we can see in this Twitter thread by Sara Ownbey Chipps…</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">What do you say when people ask you what you do? I say &quot;I&#39;m a computer programmer&quot;. Because it&#39;s all too complicated.</p>&mdash; Sara Ownbey Chipps (@SaraJChipps) <a href="https://twitter.com/SaraJChipps/status/1259968675144179714?ref_src=twsrc%5Etfw">May 11, 2020</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>While I agree with Sara that it is complicated to explain at times, deep down, it isn’t. I replied to Sara, half-jokingly, <em>“Professional Searcher” or “Professional Googler” too complicated? :smile:</em>. Later on that day, on a different thread, someone said, <em>“I do know what I’m doing. I’m damned good at Googling for answers!”</em></p>

<p>Combine these two separate threads with a random tweet of mine from Sunday.</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Even though I don't code for work everyday, I surprise myself with how much I know, remember, and/or can create when I am working by myself. My latest new (personal) project, MyEventPresentations (<a href="https://jjg.me/github-mep)">https://jjg.me/github-mep</a>), is coming along.</p>&mdash; jguadagno (@jguadagno) <a href="https://twitter.com/jguadagno/status/1259624230964133890?ref_src=twsrc%5Etfw">May 10, 2020</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>It made me realize that being a Software Engineer is not about being an expert in a programming language, knowing all the answers to every question, or how to solve every problem. It’s really about having the ability to dissect a problem and have the capacity to dig for potential solutions/fixes. Another way to look at it is trying to solve that 1000 piece jigsaw puzzle.  Sure, you have solved many of them before, but they are all different.  How you solve that jigsaw puzzle is the skill.  Do you start with corners, then the straight parts? Do you move towards the center with like shapes and colors? Over time, you add things to your mental toolbox for jigsaw puzzle-solving, and you can tackle harder jigsaw puzzle. This underlying theme is breaking the problem down into smaller, manageable pieces and putting those pieces together to solve the problem.  Problem-solving skills are not something that can easily be taught to an individual. For most people, it comes with trial and error, time, and experience. However, with that problem-solving skill and having a base understanding of how things work, a suite of programming tools, a good search engine, and the willingness to learn, you too could build that piece of software or solve that jigsaw puzzle.  With practice, you will progress in the field of software engineering.</p>

<p>To wrap up, as one of my leaders wrapped up a call with all the senior leaders explaining software engineering, <em>“Engineering is basically continuous learning… and solving problems.”</em></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Technology" /><summary type="html"><![CDATA[There has been much talk on social media about being a Software Engineer/Developer/Programmer in the last week or so, probably because we are all being safe and staying in our houses with not much else to do. Conversations have included what it takes to be an engineer, how to get started in engineering, and explaining what a ‘Software Engineer’ does.]]></summary></entry><entry><title type="html">Coding with JoeG - I’m Streaming</title><link href="https://www.josephguadagno.net/2020/04/25/coding-with-joeg-im-streaming" rel="alternate" type="text/html" title="Coding with JoeG - I’m Streaming" /><published>2020-04-25T00:00:00-07:00</published><updated>2020-04-25T00:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/04/25/coding-with-joeg-im-streaming</id><content type="html" xml:base="https://www.josephguadagno.net/2020/04/25/coding-with-joeg-im-streaming"><![CDATA[<p>I’m joining many other technology leaders and public speakers and starting to live code on <a href="https://www.twitch.tv/jguadagno" target="_blank">Twitch</a>… maybe soon to YouTube!</p>

<h2 id="content">Content</h2>

<p>Like most streams, there is always some theme.  While there will be a lot of Microsoft technologies used, <em>you go with what you know</em>, I’m going to try and be platform agnostic. We’ll cover topics from beginner to advanced. The goal is to not necessarily teach you every nuisance of a language/tool/technology but to learn, I plan on learning, some common programming practices, and principals. We’ll do this by building a contact application from scratch. Start with a clean slate, aka ‘<em>File | New</em>’, and grow the application while following some ‘best practices’.  Technologies we’ll include in the application include:</p>

<ul>
  <li>Database</li>
  <li>Unit Tests</li>
  <li>Integration Tests</li>
  <li>Load Tests</li>
  <li>An API</li>
  <li>API management</li>
  <li>Cloud Deployments</li>
  <li>Build Scripts</li>
  <li>and more…</li>
</ul>

<p>I’ve outlined how I plan to build this application already. You can check out the outline on <a href="https://github.com/jguadagno/Contacts/blob/master/outline.md" target="_blank">Github</a>.</p>

<p>As we complete tasks, and I post the video(s), I’ll update the <a href="https://github.com/jguadagno/Contacts/blob/master/outline.md" target="_blank">outline</a> with links to the video(s) and commit ids.</p>

<p>You can watch the progress of the codebase at <a href="https://github.com/jguadagno/Contacts" target="_blank">https://github.com/jguadagno/Contacts</a>.</p>

<h3 id="suggestions">Suggestions</h3>

<p>If you want to see a slightly different order or add something to the plan.  Submit a <a href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request" target="_blank">pull request</a> (PR) to the <a href="https://github.com/jguadagno/Contacts" target="_blank">repo</a>.</p>

<p>Have additional suggestions for the stream, add an <a href="https://github.com/jguadagno/CodingWithJoeG/issues" target="_blank">issue</a>.</p>

<h2 id="schedule">Schedule</h2>

<p>I’m going to start out streaming 3 days a week, Monday, Tuesday, and Thursday. I’ll start at 7 pm Eastern Daylight Time (<a href="https://www.timeanddate.com/time/zones/edt" target="_blank">EDT</a>), which is currently <a href="https://www.timeanddate.com/time/aboututc.html" target="_blank">UTC</a>-4, due to daylight saving time. When the US is not in daylight savings time, I’ll start at 7 pm Eastern Standard Time (<a href="https://www.timeanddate.com/time/zones/est" target="_blank">EST</a>), which is <a href="https://www.timeanddate.com/time/aboututc.html" target="_blank">UTC</a>-5.</p>

<h2 id="links">Links</h2>

<ul>
  <li>Stream - <a href="https://twitch.tv/jguadagno" target="_blank">https://twitch.tv/jguadagno</a></li>
  <li>Github Repository (Contacts) - <a href="https://github.com/jguadagno/Contacts" target="_blank">https://github.com/jguadagno/Contacts</a></li>
  <li>Development Flow - <a href="https://github.com/jguadagno/Contacts/blob/master/outline.md" target="_blank">https://github.com/jguadagno/Contacts/blob/master/outline.md</a></li>
  <li>YouTube Playlist - <a href="https://www.youtube.com/playlist?list=PLESGit66MnblZW06pJQwB_VPFZnOT-gG2" target="_blank">Coding with JoeG</a></li>
</ul>

<p>I hope to see you there!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term="Twitch" /><category term="Stream" /><category term="CodingWithJoeG" /><category term="Archive" /><summary type="html"><![CDATA[I’m joining many other technology leaders and public speakers and starting to live code on Twitch… maybe soon to YouTube!]]></summary></entry><entry><title type="html">Build, Sign, and Deploy NuGet Packages with Azure Pipelines</title><link href="https://www.josephguadagno.net/2020/04/12/build-sign-and-deploy-nuget-packages-with-azure-pipelines" rel="alternate" type="text/html" title="Build, Sign, and Deploy NuGet Packages with Azure Pipelines" /><published>2020-04-12T11:00:00-07:00</published><updated>2020-11-11T09:16:00-07:00</updated><id>https://www.josephguadagno.net/2020/04/12/build-sign-and-deploy-nuget-packages-with-azure-pipelines</id><content type="html" xml:base="https://www.josephguadagno.net/2020/04/12/build-sign-and-deploy-nuget-packages-with-azure-pipelines"><![CDATA[<p>In this post we’ll cover how you can use Azure Pipelines to build, test, sign, and deploy a NuGet package written with .NET Core.</p>

<h2 id="intro-to-yaml">Intro to Yaml</h2>

<p>Before we get started, you might want to brush up on <a href="https://yaml.org/" target="_blank">YAML</a>. If you do not know what YAML is, it is a human-friendly data serialization standard for all programming languages. I like to think of it as a more modern, less verbose version of XML. If YAML or learning another language/data format scares you, do be worried!  You don’t need to read/write YAML in order to use Azure Pipelines. Azure provides a UI to build out your pipeline.  That’s how I learned it!</p>

<p>If you understand YAML, check out the <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&amp;tabs=schema%2Cparameter-schema&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Azure YAML schema</a>.</p>

<h2 id="getting-started">Getting Started</h2>

<p>Sign into the Azure DevOps portal, click on <em>Pipelines</em> then <em>Pipelines</em> or visit <em>username</em>.visualstudio.com/<em>Public</em>/_build. Be sure to replace your <em>username</em> with yours or your organizations and <em>Public</em> with whatever project name want to work with created.</p>

<p>If you don’t have any pipelines created, you’ll see this.</p>

<p><img src="/assets/images/posts/build-sign-create-pipeline.png" alt="Build and Sign - Create Pipeline" class="align-center" /></p>

<p>Click ‘Create Pipeline’ to start the Pipeline Wizard.</p>

<p><img src="/assets/images/posts/build-sign-pipeline-connect.png" alt="Build and Sign - Create Pipeline Wizard - Connect" class="align-center" /></p>

<p>Choose the repository where your source code is. I used ‘Azure Repos Git’</p>

<p>Select the repository. I chose my ‘AwesomeSample’ repository</p>

<p><img src="/assets/images/posts/build-sign-pipeline-select.png" alt="Build and Sign - Create Pipeline Wizard - Select" class="align-center" /></p>

<p>Now choice your pipeline.</p>

<p><img src="/assets/images/posts/build-sign-pipeline-configure.png" alt="Build and Sign - Create Pipeline Wizard - Configure" class="align-center" /></p>

<p>If you chose, ‘Show more’, you will get some templates for common projects like .NET applications, web applications, Android applications, and more.</p>

<p>Click ‘Starter pipeline’ since we are going to build it from scratch.  This will bring use to the pipeline editor to review it.</p>

<p><img src="/assets/images/posts/build-sign-pipeline-review.png" alt="Build and Sign - Create Pipeline Wizard - Review" class="align-center" /></p>

<h3 id="pipeline-editor">Pipeline Editor</h3>

<p>While you are good to go at this point, once you click ‘Save and run’, let’s go through the editor. You’ll notice that is has a familiar feel if you have done and Visual Studio or Visual Studio Code development.  In fact, this editor has Intellisense built-in also.</p>

<p>The components of the editor:</p>

<p><img src="/assets/images/posts/build-sign-pipeline-review-annotated.png" alt="Build and Sign - Create Pipeline Wizard - Annotated Editor" class="align-center" /></p>

<p><strong>1</strong> - Repository Name</p>

<p><strong>2</strong> - Pipeline file name.  You can click here to rename the default pipeline name.  From what I can tell, most people keep the default.</p>

<p><strong>3</strong> - Variable.  This provides access to variables to use through out your pipeline. We may cover this later.</p>

<p><strong>4</strong> - Save and run.  This is how you can save and ‘execute’ or ‘run’ your pipeline. If you click on the dropdown icon, you can just save the file without running it.</p>

<p><strong>5</strong> - Show assistant.  This provides a mini wizard to chose tasks and settings.</p>

<p><strong>6</strong> - The editor.  This is the file editor. Line numbers, Intellisense, and more!</p>

<h2 id="pipeline-basics">Pipeline Basics</h2>

<p>Check the docs, <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/key-pipelines-concepts?view=azure-devops&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Key Concepts</a> for some key concepts on everything that makes up a pipeline.  We are going to cover <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/key-pipelines-concepts?view=azure-devops&amp;?wt.mc_id=DT-MVP-4024623#trigger" target="_blank">triggers</a>, <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/agents?view=azure-devops&amp;tabs=browser&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">pools</a>, variables, <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/key-pipelines-concepts?view=azure-devops&amp;?wt.mc_id=DT-MVP-4024623#step" target="_blank">steps</a>, and <a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started/key-pipelines-concepts?view=azure-devops&amp;?wt.mc_id=DT-MVP-4024623#task" target="_blank">tasks</a>.</p>

<h3 id="trigger">Trigger</h3>

<p>The trigger of a pipeline is probably exactly what you are thinking it is.  This is what will kick off or trigger the script automatically.  You can still kick off a script from this page or the pipeline page.</p>

<p>For our script, and simplicity’s sake, we are going to have the script kick off on any commits to the <em>master</em> branch.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="na">trigger</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">master</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="setup-pool">Setup Pool</h3>

<p>The pool tells Azure when VMs and ‘pooled’ resources it should use.  This varies based on the subscription model you have and are willing to pay for. For a list of the built-in agents, see the document called ‘<a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Microsoft-hosted agents</a>’.</p>

<p>Since we are planning to sign the package and the signing only works on Windows machines (at least that I could get to work). We are going to choose a vmImage of <code class="language-plaintext highlighter-rouge">windows-latest</code>.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="na">pool</span><span class="pi">:</span>
  <span class="na">vmImage</span><span class="pi">:</span> <span class="s1">'</span><span class="s">windows-latest'</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="steps">Steps</h3>

<p>Now you can think of the steps as the individual tasks that needs to happen.  In fact, you will be creating <em>tasks</em> for each thing we want to do.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="na">steps</span><span class="pi">:</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="net-version">.NET version</h3>

<p>Now let’s tell the agent what version of .NET to run</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">UseDotNet@2</span>
  <span class="na">displayName</span><span class="pi">:</span> <span class="s">Setup .NET SDK Version 3.x</span>
  <span class="na">inputs</span><span class="pi">:</span>
    <span class="na">packageType</span><span class="pi">:</span> <span class="s">sdk</span>
    <span class="na">version</span><span class="pi">:</span> <span class="s">3.x</span>
    <span class="na">includePreviewVersions</span><span class="pi">:</span> <span class="no">true</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">- task:</code> should be on the next line after the <code class="language-plaintext highlighter-rouge">steps:</code> element. The value after the <code class="language-plaintext highlighter-rouge">task</code> is the task name.  In this case, we are running the task call <em>UseDotNet</em> and version <em>2</em>.  The convention for the task name is <code class="language-plaintext highlighter-rouge">taskName@versionNumber</code>. Don’t worry, you don’t have to remember them all.  You use the assistant to generate the snippet. the <em>displayName</em> can be whatever text you want it to be. This will be shown in the pipeline progress/status page.</p>

<p><code class="language-plaintext highlighter-rouge">inputs</code> vary by tasks.  In the case, we are using <code class="language-plaintext highlighter-rouge">packageType</code>, <code class="language-plaintext highlighter-rouge">version</code>, and <code class="language-plaintext highlighter-rouge">includePreviewVersions</code>.</p>

<h3 id="build-project">Build project</h3>

<p>In order to run the unit tests and ultimately sign the package we need to build the project. Let’s create a ‘Build’ task with the following YAML.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">DotNetCoreCLI@2</span>
  <span class="na">displayName</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Build</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">Assembly'</span>
  <span class="na">inputs</span><span class="pi">:</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">build</span>
    <span class="na">versioningScheme</span><span class="pi">:</span> <span class="s">byBuildNumber</span>
    <span class="na">arguments</span><span class="pi">:</span> <span class="s1">'</span><span class="s">--configuration</span><span class="nv"> </span><span class="s">$(BuildConfiguration)'</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You’ll notice after we paste or type in the code in the editor a <em>Settings</em> item appeared.  You can click on this to get the assistant to open for that task.  It looks something like this.</p>

<p><img src="/assets/images/posts/build-sign-assistant.png" alt="Build and Sign - Task Assistant" class="align-center" /></p>

<p>The ‘Assistant’ provides a GUI for editing specifics of a task.  In most cases you can click on ‘<em>About this task</em>’ for the task documentation.</p>

<p>I bet you are wondering the <code class="language-plaintext highlighter-rouge">$(BuildConfiguration)</code> value is for the <em>arguments</em> property is?  These tasks will build the project, as the <em>command</em> input property suggests with the <em>versioningScheme</em> of <code class="language-plaintext highlighter-rouge">byBuildNumber</code>. To me, this was one of the hardest pieces to automate so I had proper <a href="https://semver.org/" target="_blank">semantic versioning</a> for the package. For this to work ‘correctly’ and automatically we needed to make some changes to the project file.  I added two project groups to my projects .csproj file.</p>

<h4 id="assembly-versioning">Assembly Versioning</h4>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre>  <span class="nt">&lt;PropertyGroup&gt;</span>
    <span class="nt">&lt;VersionMajor&gt;</span>1<span class="nt">&lt;/VersionMajor&gt;</span>
    <span class="nt">&lt;VersionMinor&gt;</span>0<span class="nt">&lt;/VersionMinor&gt;</span>
    <span class="nt">&lt;VersionBuild&gt;</span>0<span class="nt">&lt;/VersionBuild&gt;</span>
  <span class="nt">&lt;/PropertyGroup&gt;</span>

  <span class="nt">&lt;PropertyGroup&gt;</span>
    <span class="nt">&lt;VersionPrefix</span> <span class="na">Condition=</span><span class="s">" '$(BUILD_BUILDNUMBER)' == '' "</span><span class="nt">&gt;</span>$(VersionMajor).$(VersionMinor).$(VersionBuild)-local<span class="nt">&lt;/VersionPrefix&gt;</span>
    <span class="nt">&lt;VersionPrefix</span> <span class="na">Condition=</span><span class="s">" '$(BUILD_BUILDNUMBER)' != '' And '$(Configuration)' == 'Debug'"</span> <span class="nt">&gt;</span>$(VersionMajor).$(VersionMinor).$(VersionBuild)-$(BUILD_BUILDNUMBER)-preview<span class="nt">&lt;/VersionPrefix&gt;</span>
    <span class="nt">&lt;VersionPrefix</span> <span class="na">Condition=</span><span class="s">" '$(Configuration)' != 'Debug'"</span><span class="nt">&gt;</span>$(VersionMajor).$(VersionMinor).$(VersionBuild)<span class="nt">&lt;/VersionPrefix&gt;</span>
  <span class="nt">&lt;/PropertyGroup&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The first group is contains the versioning info.  Now, the engineer is responsible for versioning the project.  <strong>Note</strong>: The version number will be used for the Assembly, the NuGet package name, and NuGet versioning information.</p>

<p>The second group, defines how the version number is generated. These version numbers are based on where and how the application is being built.</p>

<table>
  <thead>
    <tr>
      <th>Version Prefix</th>
      <th>Condition</th>
      <th>Assembly Info</th>
      <th>Comments</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>First</td>
      <td><code class="language-plaintext highlighter-rouge">'$(BUILD_BUILDNUMBER)' == ''</code></td>
      <td>$(VersionMajor).$(VersionMinor).$(VersionBuild)-local</td>
      <td><code class="language-plaintext highlighter-rouge">BUILD_BUILDNUMBER</code> is only populated on the Azure build agents so it is empty when you build locally</td>
    </tr>
    <tr>
      <td>Second</td>
      <td><code class="language-plaintext highlighter-rouge">'$(BUILD_BUILDNUMBER)' != '' And '$(Configuration)' == 'Debug'"</code></td>
      <td>$(VersionMajor).$(VersionMinor).$(VersionBuild)-$(BUILD_BUILDNUMBER)-preview</td>
      <td>This means we are building the Assembly in Azure and choosing the <code class="language-plaintext highlighter-rouge">Debug</code> configuration</td>
    </tr>
    <tr>
      <td>Third</td>
      <td><code class="language-plaintext highlighter-rouge">'$(Configuration)' != 'Debug'"</code></td>
      <td>$(VersionMajor).$(VersionMinor).$(VersionBuild)</td>
      <td>This means we are building in a <code class="language-plaintext highlighter-rouge">Release</code> configuration</td>
    </tr>
  </tbody>
</table>

<h4 id="pipeline-parameter-and-variables">Pipeline Parameter and Variables</h4>

<p>Because we want the ability to indicate whether we are building the <code class="language-plaintext highlighter-rouge">Debug</code> or <code class="language-plaintext highlighter-rouge">Release</code> configuration, we are going to use ‘<a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/runtime-parameters?view=azure-devops&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">parameters</a>’ and ‘<a href="https://docs.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&amp;tabs=yaml%2Cbatch&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">variables</a>’</p>

<p>Let’s go back to before the <code class="language-plaintext highlighter-rouge">trigger</code>, around line 5 or 6 and past this parameter yaml.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="na">parameters</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">buildConfig</span>
    <span class="na">displayName</span><span class="pi">:</span> <span class="s">Build Configuration to Use</span>
    <span class="na">default</span><span class="pi">:</span> <span class="s">Debug</span>
    <span class="na">values</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">Debug</span>
    <span class="pi">-</span> <span class="s">Release</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This creates a parameter in the pipeline call <strong>buildConfig</strong>.  <strong>buildConfig</strong> will be displayed with the text of <em>Build Configuration to Use</em>. The <strong>buildConfig</strong> parameter will only have two choices of <code class="language-plaintext highlighter-rouge">Debug</code> and <code class="language-plaintext highlighter-rouge">Release</code> and will be presented as a dropdown list.  We set the default selection to <code class="language-plaintext highlighter-rouge">Debug</code>. Having a default is important if you are running the pipeline on any trigger.  Setting the default for the build configuration to <code class="language-plaintext highlighter-rouge">Debug</code> ensures that I do not commit <code class="language-plaintext highlighter-rouge">Release</code> builds unintentionally.</p>

<p><strong>NOTE</strong>: The need to a <strong>buildConfig</strong> is not needed if you want to control the build configuration by branches.  That is a little beyond this post.</p>

<p>Now, that we have the parameter, we need to create a variable to place the selected configuration into.</p>

<p>Below the <code class="language-plaintext highlighter-rouge">pool</code> and before the <code class="language-plaintext highlighter-rouge">steps</code>, around line 19, insert this YAML to create the variable.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="na">variables</span><span class="pi">:</span>
  <span class="na">buildConfiguration</span><span class="pi">:</span> <span class="s">$</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This now creates the variable <strong>buildConfigurations</strong> based on the <strong>buildConfig</strong> parameter</p>

<h3 id="test-the-project">Test the project</h3>

<p>We’ll want to make sure that all of unit tests for our project pass before we attempt to sign the package.  Jump back down to the end of the file, around line 37, and enter the test task</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">DotNetCoreCLI@2</span>
  <span class="na">displayName</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Run</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">Unit</span><span class="nv"> </span><span class="s">Tests'</span>
  <span class="na">inputs</span><span class="pi">:</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s1">'</span><span class="s">test'</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="package-the-project">Package the Project</h3>

<p>Now that the project has been built and the unit tests pass, let’s package the project.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">DotNetCoreCLI@2</span>
  <span class="na">displayName</span><span class="pi">:</span> <span class="s">Package up the Assembly</span>
  <span class="na">inputs</span><span class="pi">:</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s1">'</span><span class="s">pack'</span>
    <span class="na">packagesToPack</span><span class="pi">:</span> <span class="s1">'</span><span class="s">./src/*.csproj'</span>
    <span class="na">nobuild</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">verbosityPack</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Normal'</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <strong>nobuild</strong> tells <em>dotnet</em> to not build the project. Building the project is not necessary since it was built so that we can run the unit tests.</p>

<h3 id="sign-the-package">Sign the Package</h3>

<p>Phew, the project is packaged, let’s sign it.</p>

<p>We are going to use <a href="https://azure.microsoft.com/en-us/services/key-vault/?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault</a> to use our certificate to sign the package. If you haven’t setup a certificate in a key vault yet, check out <a href="/2020/04/04/setup-code-signing-certificates-in-azure-key-vault">Setup Code Signing Certificates in Azure Key Vault</a></p>

<p>To assist us in using Azure Key Vault certificate signing, we are going to use a community created tool, <a href="https://github.com/novotnyllc/NuGetKeyVaultSignTool" target="_blank">NuGetKeyVaultSignTool</a>.  This tool uses some of the Key Vault APIs.  As a result, we are going to need <em>client id</em> and <em>client secret</em>. For more on setting up an application in Azure to access the Key Vault through an API check out <a href="/2020/04/07/setup-an-azure-application-with-permissions-to-enable-certificate-signing">Setup an Azure Application with Permissions to Enable Certificate Signing</a>.</p>

<p>We are going to need the <em>client id</em>, <em>client secret</em>, and a few other values for our building and signing of the package. Since you <strong><em>NEVER EVER</em></strong> want to store any secrets in your source code repository, we are going to need an alternate way to access those secrets and keep them out of our source code repository.  Azure Pipelines offers Variable for that.  So let’s create some.</p>

<h4 id="setup-the-variables">Setup the Variables</h4>

<p>You’ll probably want to open another tab for this step. In the new tab, navigate to the Azure DevOps portal, click on <em>Pipelines</em>, then <em>Library</em> or visit <em>username</em>.visualstudio.com/<em>Public</em>/_library?itemType=VariableGroups. Be sure to replace your <em>username</em> with yours or your organizations and <em>Public</em> with whatever project name you created.</p>

<p>You should see a variable <em>New variable group</em> screen.</p>

<p><img src="/assets/images/posts/build-sign-variable-groups.png" alt="Build and Sign - Creating a Variable Group" class="align-center" /></p>

<ul>
  <li>Click on ‘+ Variable Group’</li>
  <li>Enter the following</li>
</ul>

<table>
  <thead>
    <tr>
      <th>Property</th>
      <th>Value</th>
      <th>Comments</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Variable Group Name</td>
      <td><code class="language-plaintext highlighter-rouge">CodeSigning</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Description</td>
      <td><code class="language-plaintext highlighter-rouge">This is the group for handling all of the secrets for signing the code</code></td>
      <td> </td>
    </tr>
    <tr>
      <td>Allow access to all pipelines</td>
      <td><code class="language-plaintext highlighter-rouge">Selected</code></td>
      <td>I chose <code class="language-plaintext highlighter-rouge">Yes</code>, you choose what is correct for your environment</td>
    </tr>
    <tr>
      <td>Link secrets from an Azure key vault as variables</td>
      <td><code class="language-plaintext highlighter-rouge">Selected</code></td>
      <td>This is optional, I chose to do it so only Key Vault admins can see the secrets</td>
    </tr>
    <tr>
      <td>Azure subscription</td>
      <td><em>your azure subscription</em></td>
      <td>Optional, if you chose to link secrets.  You will have to authorize the secrets if you haven’t already</td>
    </tr>
    <tr>
      <td>Key Vault Name</td>
      <td><em>your vault</em></td>
      <td>Optional, if you chose to link secrets.  You will have to authorize the secrets if you haven’t already</td>
    </tr>
  </tbody>
</table>

<p>Once the ‘Link secrets from an Azure Key Vault as variables’ is complete and you click ‘+ Add’ under variables, you will be presented with a dialog to ‘Choose secrets’, similar to this one.</p>

<p><img src="/assets/images/posts/build-sign-variable-add.png" alt="Build and Sign - Adding a linked variable" class="align-center" /></p>

<p>I added the client id and secrets to my vault already, so I selected them and clicked ok.</p>

<p>Now click, Save.</p>

<h4 id="sign-package">Sign Package</h4>

<p>Back to the pipeline editor tab.</p>

<p>We’ll first need to add the new created <em>client id</em> and <em>client secret</em> variables as a variable in the pipeline.  Go back to the <strong>variables</strong> section of our pipeline and add <code class="language-plaintext highlighter-rouge">- group: CodeSigning</code> to the section. Because we added a group the original variable <strong>buildConfiguration</strong> needs to be changed to a <em>name</em> <em>value</em> format. Once done it should look like this.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="na">variables</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">buildConfiguration</span>
  <span class="na">value</span><span class="pi">:</span> <span class="s">$</span>
<span class="pi">-</span> <span class="na">group</span><span class="pi">:</span> <span class="s">CodeSigning</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Since we are accessing the library now, this pipeline is going to need extra permissions.</p>

<p><img src="/assets/images/posts/build-sign-pipeline-needs-permission.png" alt="Build and Sign - Pipeline needs permissions" class="align-center" /></p>

<ul>
  <li>Click ‘View’</li>
  <li>Click ‘Permit’</li>
</ul>

<p>You’ll be prompted to confirm the permission.</p>

<p><img src="/assets/images/posts/build-sign-pipeline-needs-permission-confirm.png" alt="Build and Sign - Pipeline Confirm Permissions" class="align-center" /></p>

<ul>
  <li>Click ‘Permit’</li>
</ul>

<p>By granting permission, a new hidden task happens to download the certificates.  In my case, I get a ‘Download secrets: PersonalSecrets’.</p>

<p>We first need to install the <a href="https://github.com/novotnyllc/NuGetKeyVaultSignTool" target="_blank">NuGetKeyVaultSignTool</a>, so let’s create a task for it.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">DotNetCoreCLI@2</span>
  <span class="na">displayName</span><span class="pi">:</span> <span class="s">Install NuGetKeyVaultSignTool</span>
  <span class="na">inputs</span><span class="pi">:</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s1">'</span><span class="s">custom'</span>
    <span class="na">custom</span><span class="pi">:</span> <span class="s1">'</span><span class="s">tool'</span>
    <span class="na">arguments</span><span class="pi">:</span> <span class="s1">'</span><span class="s">install</span><span class="nv"> </span><span class="s">--tool-path</span><span class="nv"> </span><span class="s">.</span><span class="nv"> </span><span class="s">NuGetKeyVaultSignTool'</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here we created used the custom option for the DotNetCoreCli task to install the tool.</p>

<p>Now we’ll need to create a script to call the tool</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">PowerShell@2</span>
  <span class="na">displayName</span><span class="pi">:</span> <span class="s">Running NuGetKeyVaultSign</span>
  <span class="na">inputs</span><span class="pi">:</span>
    <span class="na">targetType</span><span class="pi">:</span> <span class="s1">'</span><span class="s">inline'</span>
    <span class="na">script</span><span class="pi">:</span> <span class="pi">|</span>
      <span class="s">.\NuGetKeyVaultSignTool sign ./src/**/*.nupkg `</span>
      <span class="s">--file-digest "sha256" `</span>
      <span class="s">--timestamp-rfc3161 "https://timestamp.digicert.com" `</span>
      <span class="s">--timestamp-digest "sha256" `</span>
      <span class="s">--azure-key-vault-url "https://&lt;yourvaultname&gt;.vault.azure.net/" `</span>
      <span class="s">--azure-key-vault-tenant-id "92a85e53-affe-4d2d-ac5d-99383f88e2e3" `</span>
      <span class="s">--azure-key-vault-client-id "$(azure-key-vault-client-id)" `</span>
      <span class="s">--azure-key-vault-client-secret "$(azure-key-vault-client-secret)" `</span>
      <span class="s">--azure-key-vault-certificate "&lt;certificateName&gt;"</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>NOTE</strong> There a backticks “`” at the end of every line.  This is used so the Powershell tasks knows that we are continuing on the next line.</p>

<p>The first part of the Powershell shell task is the execute the <code class="language-plaintext highlighter-rouge">NuGetKeyVaultSignTool</code> with the <code class="language-plaintext highlighter-rouge">sign</code> option. The <code class="language-plaintext highlighter-rouge">./src/**/*.nupkg</code> argument will get us the built package. The rest of the arguments go into what the tool needs to sign the package suing Azure Key Vault.</p>

<table>
  <thead>
    <tr>
      <th>Argument</th>
      <th>Value</th>
      <th>Comment</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>file-digest</strong></td>
      <td><code class="language-plaintext highlighter-rouge">sha256</code></td>
      <td>This shouldn’t need to change depending on your certificate</td>
    </tr>
    <tr>
      <td><strong>timestamp-rfc3161</strong></td>
      <td><code class="language-plaintext highlighter-rouge">https://timestamp.digicert.com</code></td>
      <td>This shouldn’t need to change unless you get your certificate from another source</td>
    </tr>
    <tr>
      <td><strong>timestamp-digest</strong></td>
      <td><code class="language-plaintext highlighter-rouge">sha256</code></td>
      <td>This shouldn’t need to change depending on your certificate</td>
    </tr>
    <tr>
      <td><strong>azure-key-vault-url</strong></td>
      <td><code class="language-plaintext highlighter-rouge">https://&lt;yourvaultname&gt;.vault.azure.net/</code></td>
      <td>Replace this vault with the Url for your Azure Key Vault</td>
    </tr>
    <tr>
      <td><strong>azure-key-vault-tenant-id</strong></td>
      <td><code class="language-plaintext highlighter-rouge">92a85e53-affe-4d2d-ac5d-99383f88e2e3</code></td>
      <td>Replace this with the Azure Tenant Id that this Key Vault is in</td>
    </tr>
    <tr>
      <td><strong>azure-key-vault-client-id</strong></td>
      <td><code class="language-plaintext highlighter-rouge">$(azure-key-vault-client-id)</code></td>
      <td>Pulls the value from the CodeSigning group and Azure Key Vault</td>
    </tr>
    <tr>
      <td><strong>azure-key-vault-client-secret</strong></td>
      <td><code class="language-plaintext highlighter-rouge">$(azure-key-vault-client-secret)</code></td>
      <td>Pulls the value from the CodeSigning group and Azure Key Vault</td>
    </tr>
    <tr>
      <td><strong>azure-key-vault-certificate</strong></td>
      <td><code class="language-plaintext highlighter-rouge">&lt;certificateName&gt;</code></td>
      <td>Replace with the name of your certificate</td>
    </tr>
  </tbody>
</table>

<h3 id="azure-artifact">Azure Artifact</h3>

<p>If you haven’t setup up an Azure Artifact repository you can ready the post <a href="/2020/04/04/setup-azure-artifacts-to-host-nuget-packages">Setup Azure Artifacts to Host Your NuGet Packages</a> to get started.</p>

<p>The last step is publishing or ‘pushing’ the package off to Azure Artifacts. For this, we are going to continue to use the DotNetCli task with the <em>push</em> command.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">DotNetCoreCLI@2</span>
  <span class="na">displayName</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Publish</span><span class="nv"> </span><span class="s">the</span><span class="nv"> </span><span class="s">NuGet</span><span class="nv"> </span><span class="s">Package'</span>
  <span class="na">inputs</span><span class="pi">:</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s1">'</span><span class="s">push'</span>
    <span class="na">packagesToPush</span><span class="pi">:</span> <span class="s1">'</span><span class="s">$(Build.ArtifactStagingDirectory)/*.nupkg'</span>
    <span class="na">nuGetFeedType</span><span class="pi">:</span> <span class="s1">'</span><span class="s">internal'</span>
    <span class="na">publishVstsFeed</span><span class="pi">:</span> <span class="s1">'</span><span class="s">&lt;libraryGuid&gt;'</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Argument</th>
      <th>Value</th>
      <th>Comment</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>command</strong></td>
      <td><code class="language-plaintext highlighter-rouge">push</code></td>
      <td> </td>
    </tr>
    <tr>
      <td><strong>packagesToPush</strong></td>
      <td><code class="language-plaintext highlighter-rouge">$(Build.ArtifactStagingDirectory)/*.nupkg</code></td>
      <td>The <code class="language-plaintext highlighter-rouge">Build.ArtifactStagingDirectory</code> is an global variable that Azure Pipelines exposes to indicate the directory where build artifacts are placed</td>
    </tr>
    <tr>
      <td><strong>nuGetFeedType</strong></td>
      <td><code class="language-plaintext highlighter-rouge">internal</code></td>
      <td>Internal is used if the Artifact is located in the same organization</td>
    </tr>
    <tr>
      <td><strong>publishVstsFeed</strong></td>
      <td><code class="language-plaintext highlighter-rouge">&lt;libraryGuid&gt;</code></td>
      <td>The GUID that identifies the library.  It’s not easy to find, so after you paste the YAML into the editor. Use the assistance to edit the tasks</td>
    </tr>
  </tbody>
</table>

<h2 id="running-the-pipeline">Running the Pipeline</h2>

<p>We started this task to get triggered whenever there was a commit to the master branch.  Once we click save in the editor, if you haven’t been saving at each task, we will be prompted for a commit message.</p>

<ul>
  <li>Click the ‘Save’ button, or ‘Save and Run’ if you haven’t been saving along with the post.</li>
</ul>

<p><img src="/assets/images/posts/build-sign-save-pipeline.png" alt="Build and Sign - Save Pipeline" class="align-center" /></p>

<ul>
  <li>Enter a commit message</li>
  <li>Any optional text (optional)</li>
  <li>‘Commit directly to the master branch’ for this post, should be selected</li>
  <li>Click ‘Save’</li>
</ul>

<p>At this point the commit is being saved and a pipeline run is being triggered. Open up a new tab with the pipeline showing.</p>

<p><img src="/assets/images/posts/build-sign-pipeline-view.png" alt="Build and Sign - Pipeline View" class="align-center" /></p>

<p>You’ll see the repository name, ‘AwesomeSample’ in this case, with the Last Run. This image in front of the repository name indicates the start of the run.  In the case of the image above, it’s being queued.  If it fails, you will get an <img src="/assets/images/posts/azure-red-x.png" alt="red X" /> . If you click on the commit message, in this case <em>#20200412.1 - Blog post complete</em>, you will get to the job details to see what went wrong.</p>

<p><img src="/assets/images/posts/build-sign-pipeline-job-failed.png" alt="Build and Sign - Pipeline Job Failed" class="align-center" /></p>

<p>This shows us that there was a failure with one of the dotnet.exe tasks.  If you click on the Job below, you can see exactly what happened.</p>

<p><img src="/assets/images/posts/build-sign-pipeline-job-failed-details.png" alt="Build and Sign - Pipeline Job Failed with Details" class="align-center" /></p>

<p>We see that the task ‘<em>Build the Assembly</em>’ failed, if you click on it, you will see the details.</p>

<p>This was just for illustration, I had the paths and folders different from what the pipeline scripts were expecting. In addition, I was missing the Visual Studio solution, otherwise you have to update the tasks with folder paths.  If you build is successful, you will see a <img src="/assets/images/posts/azure-green-check.png" alt="green check mark" /> to the job. And if you click on the job you’ll see <img src="/assets/images/posts/azure-green-check.png" alt="green check mark" /> next to all of the tasks.</p>

<p><img src="/assets/images/posts/build-sign-pipeline-success.png" alt="Build and Sign - Pipeline Job Success" class="align-center" /></p>

<p>Oh, and you’ll get an email on the success (and failures).</p>

<h2 id="checking-out-the-artifact">Checking out the Artifact</h2>

<p>Now that we published the package, we can view it in our Artifacts</p>

<p>Click on ‘Artifacts’ and you will now see the AwesomeSample version 1.0.0-<code class="language-plaintext highlighter-rouge">&lt;builddate&gt;</code>.<code class="language-plaintext highlighter-rouge">&lt;buildNumber&gt;</code>-preview.  The version number and name correlates to the values and settings in our csproj as we outlined in the <strong><em>Assembly Versioning</em></strong> part of this post.</p>

<p>Click on the artifact name and up will get instructions on how to add it to your .NET application via NeGet.  If you click on the ‘Versions’ button on the toolbar you will see more details about it.</p>

<h2 id="creating-a-release-build">Creating a ‘Release’ Build</h2>

<p>Some would say that the release builds should be part of workflow, and I agree.  However, we are trying to this in ‘one’ blog posts.</p>

<ul>
  <li>Click on the ‘Pipelines’ in Azure DevOps</li>
  <li>Click on ‘Awesome Sample’</li>
  <li>Click on ‘Run Pipeline’</li>
</ul>

<p><img src="/assets/images/posts/build-sign-pipeline-run.png" alt="Build and Sign - Pipeline Run Dialog" class="align-center" /></p>

<p>You’ll see the ‘<em>Build Configuration to Use</em>’ parameter that we created earlier.</p>

<ul>
  <li>Chose ‘Release’</li>
  <li>Click ‘Run’</li>
</ul>

<p>This will take your to the job details page.  Assuming everything runs successfully, you will get a green checkmark and we can see the release build in the Artifacts.</p>

<ul>
  <li>Click on ‘Artifacts’</li>
</ul>

<p>You’ll notice that the ‘AwesomeSample’ it is now labeled <em>Version 1.0.0</em>.  If you click on ‘AwesomeSample’, then versions,  you’ll see 2 versions.  Version 1.0.0 is the currently selected one.</p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>That’s it! I know that was a lot.  But there was a lot of detail to get us up and running.  If you don’t want to piece the whole post together, you can see the gist of azure-pipeline.yml at:</p>

<script src="https://gist.github.com/b99bec48d4ecde8b4cec72b119fbdcfa.js"> </script>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET Core" /><category term="NuGet" /><category term="Azure" /><category term="Pipeline" /><category term="Key Vault" /><category term="Azure Artifacts" /><category term="Certificate" /><summary type="html"><![CDATA[Let's take a look how you can build an Azure pipeline that will build your NuGet package, sign it, then deploy it to Azure Artifacts]]></summary></entry><entry><title type="html">Setup an Azure Application with Permissions to Enable Certificate Signing</title><link href="https://www.josephguadagno.net/2020/04/07/setup-an-azure-application-with-permissions-to-enable-certificate-signing" rel="alternate" type="text/html" title="Setup an Azure Application with Permissions to Enable Certificate Signing" /><published>2020-04-07T04:00:00-07:00</published><updated>2020-04-07T04:00:00-07:00</updated><id>https://www.josephguadagno.net/2020/04/07/setup-an-azure-application-with-permissions-to-enable-certificate-signing</id><content type="html" xml:base="https://www.josephguadagno.net/2020/04/07/setup-an-azure-application-with-permissions-to-enable-certificate-signing"><![CDATA[<p>In a previous <a href="/2020/04/04/setup-code-signing-certificates-in-azure-key-vault">post</a>, I walked through how to upload a code signing certificate to Azure Key Vault.  This post will walk you through setting up the Azure “application” so that our Azure Pipeline (future post) will be able to sign our NuGet package without having to download the package.  Thanks to <a href="https://twitter.com/clairernovotny/status/1246876013050712064" target="_blank">Claire</a> for the advice.</p>

<h2 id="create-the-application">Create the Application</h2>

<ul>
  <li>Sign into the portal</li>
  <li>Search for <code class="language-plaintext highlighter-rouge">App Registrations</code></li>
  <li>Click ‘+ New Registration’</li>
  <li>For the name, enter something easy to remember. I entered <code class="language-plaintext highlighter-rouge">SupportLibraries_Pipeline</code></li>
  <li>For ‘Supported account types’, there are three options</li>
</ul>

<blockquote>
  <p><strong>Accounts in this organizational directory only (Default Directory only - Single tenant)</strong></p>

  <p>All user and guest accounts in your directory can use your application or API.
Use this option if your target audience is internal to your organization.</p>

  <p><strong>Accounts in any organizational directory (Any Azure AD directory - Multitenant)</strong></p>

  <p>All users with a work or school account from Microsoft can use your application or API. This includes schools and businesses that use Office 365.
Use this option if your target audience is business or educational customers and to enable multitenancy.</p>

  <p><strong>Personal Microsoft accounts only</strong></p>

  <p>Personal accounts that are used to sign in to services like Xbox and Skype.
Use this option to target the widest set of Microsoft identities.
Due to temporary differences in functionality, you may see errors if you attempt to switch between supported audiences after registering the application.</p>
</blockquote>

<ul>
  <li>For pipeline signatures we’ll want to select <code class="language-plaintext highlighter-rouge">Accounts in this organizational directory only (Default Directory only - Single tenant)</code></li>
  <li>For ‘Platform configuration, let’s chose <code class="language-plaintext highlighter-rouge">Background process and Automation (Daemon) Application</code></li>
  <li>Click ‘Register’</li>
</ul>

<p>Once the creation is complete, you will have to select how this application is going to identify themselves to the authenticate service. Since the tool we are going to use <a href="https://github.com/novotnyllc/NuGetKeyVaultSignTool" target="_blank">NuGetKeyVaultSignTool</a> expects a ClientId and Secret, let’s create one.</p>

<ul>
  <li>Select ‘+ New client secret’.</li>
</ul>

<p><img src="/assets/images/posts/pipeline-create-client-id.png" alt="Pipeline - Creating the Client Id" class="align-center" /></p>

<ul>
  <li>Enter a description and chose the expiration date.  I chose to never expire the client id since this will only be used for the pipeline.</li>
  <li>Click ‘Add’</li>
  <li>Copy the secret value.  You will need the later. As mentioned in the image below and one top of the blade, the client secret wont be available to be retrieved again once you leave this blade.</li>
</ul>

<p><img src="/assets/images/posts/pipeline-save-secret.png" alt="Pipeline - Remember to Save the Secret" class="align-center" /></p>

<h2 id="assign-the-contributor-role-to-the-application">Assign the Contributor role to the Application</h2>

<p>Now that we have an application setup in our directory, we need to assign it to a role so it can have access to the certificates.</p>

<ul>
  <li>Click ‘Home’</li>
  <li>Click ‘Subscriptions’</li>
  <li>Select your subscription</li>
  <li>Click on ‘Access Control (IAM)</li>
  <li>Click on ‘Add’ in the ‘Add a role assignment’ box</li>
  <li>Select the role <code class="language-plaintext highlighter-rouge">Contributor</code></li>
  <li>For ‘Assign access to’ select <code class="language-plaintext highlighter-rouge">Azure AD user, group, or service principal</code></li>
  <li>Select, enter the name you called the application.
    <ul>
      <li>The selected choice should appear below</li>
    </ul>
  </li>
  <li>Click ‘Save’</li>
</ul>

<h2 id="give-the-application-access-to-the-azure-key-vault">Give the Application Access to the Azure Key Vault</h2>

<p>Now that we established the application and assigned it to the <em>contributor</em> role, we need to give that application and role access to the vault.</p>

<ul>
  <li>In the portal, navigate to the vault</li>
  <li>Click on ‘Access Policies’</li>
  <li>Click on ‘+ Add Access Policy’</li>
</ul>

<p><img src="/assets/images/posts/pipeline-add-access-policy.png" alt="Pipeline - Add Access Policy" class="align-center" /></p>

<h3 id="required-permissions">Required Permissions</h3>

<table>
  <thead>
    <tr>
      <th>Permission Type</th>
      <th>Required Permissions</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Key Permissions</td>
      <td><em>Sign</em></td>
    </tr>
    <tr>
      <td>Certificate Permissions</td>
      <td><em>Get</em></td>
    </tr>
  </tbody>
</table>

<ul>
  <li>For the ‘Select Principal’, select the application you created earlier.</li>
  <li>Click ‘Add’</li>
  <li>Then click ‘Save’</li>
</ul>

<p>You’ll now see the application listed in the Access Policies.</p>

<p>That’s should be it.  We’ll tie this application to our pipeline in a later post.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET Core" /><category term="NuGet" /><category term="Azure" /><category term="Pipeline" /><category term="Key Vault" /><category term="Azure Artifacts" /><category term="Certificate" /><summary type="html"><![CDATA[In a previous post, I walked through how to upload a code signing certificate to Azure Key Vault. This post will walk you through setting up the Azure “application” so that our Azure Pipeline (future post) will be able to sign our NuGet package without having to download the package. Thanks to Claire for the advice.]]></summary></entry><entry><title type="html">Setup Azure Artifacts to Host Your NuGet Packages</title><link href="https://www.josephguadagno.net/2020/04/04/setup-azure-artifacts-to-host-nuget-packages" rel="alternate" type="text/html" title="Setup Azure Artifacts to Host Your NuGet Packages" /><published>2020-04-04T19:30:00-07:00</published><updated>2020-04-04T19:30:00-07:00</updated><id>https://www.josephguadagno.net/2020/04/04/setup-azure-artifacts-to-host-nuget-packages</id><content type="html" xml:base="https://www.josephguadagno.net/2020/04/04/setup-azure-artifacts-to-host-nuget-packages"><![CDATA[<p>Let’s take a look how you can build an Azure pipeline that will build your NuGet package, sign it, then deploy it to Azure Artifacts
<!--more--></p>

<p>According the Azure Artifacts <a href="https://azure.microsoft.com/en-us/services/devops/artifacts/?wt.mc_id=DT-MVP-4024623" target="_blank">site</a>, Azure Artifacts provides a service to “Create and share Maven, npm, NuGet, and Python package feeds from public and private sources”. In this post, I’ll focus on setting up a repository to host my own private NuGet packages.</p>

<p>Just like most services provided with Azure their maybe a cost to using it.  Azure Artifacts provide both free and paid options, please visit the <a href="https://azure.microsoft.com/en-us/pricing/details/devops/azure-devops-services/?wt.mc_id=DT-MVP-4024623" target="_blank">pricing for Azure DevOps</a> page for the latest on costs for using this service.</p>

<h2 id="setup">Setup</h2>

<p>First step is signing up for Azure Artifacts is to setup an Azure DevOps account/organization. Check out the <a href="https://docs.microsoft.com/en-us/azure/devops/user-guide/sign-up-invite-teammates?view=azure-devops?wt.mc_id=DT-MVP-4024623" target="_blank">Quick Start</a> guide, to get started.</p>

<p>Now that you have an organization you’ll need to sign into it. The url should look something like <strong>dev.azure.com/<em>organization-name</em></strong>, in my case, it’s <strong>dev.azure.com/jguadagno</strong>.</p>

<h3 id="create-a-project">Create a Project</h3>

<p>Artifacts, in Azure are hosted as part of a project.  Let’s go and create a project for this.  You should a ‘+ New project’ button on your organization page.  Highlighted below.</p>

<p><img src="/assets/images/posts/artifacts-new-project.png" alt="Azure Artifacts - New Project" class="align-center" /></p>

<p>Click the ‘+ New Project’ button and you will be prompted with the <em>new project</em> dialog.</p>

<p><img src="/assets/images/posts/artifacts-create-new-project.png" alt="Azure Artifacts - Create a New Project" class="align-center" /></p>

<p>Enter a <code class="language-plaintext highlighter-rouge">project name</code>, <code class="language-plaintext highlighter-rouge">description</code>, and <code class="language-plaintext highlighter-rouge">visibility</code> for the project.  If you want to change the default version control of <em>git</em> and the default work item process of <em>agile</em> click the <strong>Advanced</strong> option.</p>

<p>Since I am going to be using this project for future demos, I am going to make it public. I’ve entered the following for this new project.</p>

<table>
  <thead>
    <tr>
      <th>Field Name</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Project name</td>
      <td>Public</td>
    </tr>
    <tr>
      <td>Description</td>
      <td>This project is used to demonstrate different aspects of Azure DevOps for blog posts and demos at <a href="/">JosephGuadagno.net</a></td>
    </tr>
    <tr>
      <td>Visibility</td>
      <td>Public</td>
    </tr>
  </tbody>
</table>

<p>And I am leaving the defaults for <code class="language-plaintext highlighter-rouge">version control</code> and <code class="language-plaintext highlighter-rouge">work item process</code>.</p>

<p><strong>NOTE</strong>: The default for an organization is <em>private</em>.  You have to edit your organizational security policy to go public.  For most people and organizations that you don’t want to be public.  I’m only doing this for the demo.  <em>Famous last words</em> :smile:</p>

<h2 id="artifacts">Artifacts</h2>

<p>After creating the project, Azure DevOps takes you right to the Project home page.  You can access the projects Artifacts section using the table of contents to the left.</p>

<p><img src="/assets/images/posts/artifacts-table-of-contents.png" alt="Azure Artifacts - Project Table of Contents" class="align-center" /></p>

<p>Earlier I said that Azure Artifacts are as part of a project, while that is partially true, for the most part it isn’t.  You can see in the image below that after I clicked on <em>Artifacts</em> I see one of my other feeds (<em>SupportLibraries</em>).  The separate project for me is so I can separate the repositories for each package.  In theory, you can do that for any project and/or repository. I just like them separate.</p>

<p><img src="/assets/images/posts/artifacts-feeds.png" alt="Azure Artifacts - Feed View" class="align-center" /></p>

<h3 id="create-the-feed">Create the Feed</h3>

<p>Let’s create a feed.  A feed, in NuGet terms, is your repository.  This is where you put all of your NuGet packages.</p>

<p>Click the ‘+ Create Feed’ button.</p>

<p><img src="/assets/images/posts/artifacts-create-feed.png" alt="Azure Artifacts - Create Feed" class="align-center" /></p>

<p>I’ve entered the following for the new feed.  Keep in mind, as the note states beneath the <code class="language-plaintext highlighter-rouge">name</code> field, this name will appear in the URL for your feed.</p>

<table>
  <thead>
    <tr>
      <th>Field Name</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Name</td>
      <td>SharedPackages</td>
    </tr>
    <tr>
      <td>Visibility</td>
      <td>Public</td>
    </tr>
  </tbody>
</table>

<p><strong>Note</strong>: you can not change the visibility of this feed since it is tied to the public ‘<em>Public</em>’ project.</p>

<p>Click ‘Create’</p>

<p>Once the creation is done, you are ready to connect to the feed.</p>

<p><img src="/assets/images/posts/artifacts-connect-to-feed.png" alt="Azure Artifacts - Connect to the Feed" class="align-center" /></p>

<h2 id="connect-to-the-feed">Connect to the Feed</h2>

<p>I’m not going to the document how you connect to the feed.  If you click on ‘Connect to Feed’ you will different options depending on the type of package management tool you are using.</p>

<p>Instructions are provided for</p>

<ul>
  <li><a href="https://www.jetbrains.com/help/rider/Using_NuGet.html#sources" target="_blank">NuGet</a>
    <ul>
      <li>dotnet</li>
      <li>NuGet.exe</li>
      <li>Visual Studio</li>
    </ul>
  </li>
  <li><a href="https://docs.microsoft.com/en-us/azure/devops/artifacts/get-started-npm?view=azure-devops&amp;tabs=windows&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">npm</a>
    <ul>
      <li>npm</li>
    </ul>
  </li>
  <li><a href="https://docs.microsoft.com/en-us/azure/devops/artifacts/get-started-maven?view=azure-devops&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Maven</a>
    <ul>
      <li>Maven</li>
      <li>Gradle</li>
    </ul>
  </li>
  <li><a href="https://docs.microsoft.com/en-us/azure/devops/artifacts/quickstarts/python-packages?view=azure-devops&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Python</a>
    <ul>
      <li>pip</li>
      <li>twine</li>
    </ul>
  </li>
  <li><a href="https://docs.microsoft.com/en-us/azure/devops/artifacts/quickstarts/universal-packages?view=azure-devops&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Universal</a>
    <ul>
      <li>Universal packages</li>
    </ul>
  </li>
</ul>

<p><em>Universal</em> gives you command line instructions for a ‘catch all’ scenario to get the package(s) onto your machine if the package manager you are using doesn’t have instructions on the site.</p>

<h3 id="jetbrains-rider">JetBrains Rider</h3>

<p>Instructions for <a href="https://www.jetbrains.com/" target="_blank">JetBrains</a> <a href="https://www.jetbrains.com/rider/" target="_blank">Rider</a> are not included. I could only assume it’s not included because it’s a competing product to Visual Studio.  In case your google-fu is off.  You can manage your NUGet packages in Rider as documented at <a href="https://www.jetbrains.com/help/rider/Using_NuGet.html#sources" target="_blank">Manage package sources</a>. For the values for <code class="language-plaintext highlighter-rouge">packageSources</code>, click on ‘Connect to Feed’, then select ‘NuGet.exe’.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET Core" /><category term="NuGet" /><category term="Azure" /><category term="Pipeline" /><category term="Key Vault" /><category term="Azure Artifacts" /><summary type="html"><![CDATA[Let’s take a look how you can build an Azure pipeline that will build your NuGet package, sign it, then deploy it to Azure Artifacts]]></summary></entry><entry><title type="html">Setup Code Signing Certificates in Azure Key Vault</title><link href="https://www.josephguadagno.net/2020/04/04/setup-code-signing-certificates-in-azure-key-vault" rel="alternate" type="text/html" title="Setup Code Signing Certificates in Azure Key Vault" /><published>2020-04-04T11:00:00-07:00</published><updated>2020-11-21T18:16:00-07:00</updated><id>https://www.josephguadagno.net/2020/04/04/setup-code-signing-certificates-in-azure-key-vault</id><content type="html" xml:base="https://www.josephguadagno.net/2020/04/04/setup-code-signing-certificates-in-azure-key-vault"><![CDATA[<p>Let’s take a look at how you can set up and configure Azure Key Vault to store Code Signing Certificates
<!--more--></p>

<p>In this post, we’ll take a look at how to store Code Signing Certificates in an Azure Key Vault.  In a future post we’ll use this certificate to sign a NuGet package to ultimately deploy to <a href="/2020/04/04/setup-azure-artifacts-to-host-nuget-packages">Azure Artifacts</a>.</p>

<h3 id="code-signing-certificate">Code Signing Certificate</h3>

<p>What is a code signing certificate you ask? I’m glad you asked! According to <a href="https://www.globalsign.com/en/code-signing-certificate" target="_blank">DigiCert</a> code signing certificates are used by software developers to digitally sign apps, drivers, and software programs as a way for end-users to verify that the code they receive has not been altered or compromised by a third party. They include your signature, your company’s name, and if desired, a timestamp.</p>

<p>Essentially, they are a way to validate that source code, packages, executables, etc., comes from who it says it’s from, similarly to how certificates on a web site authenticate the web site when you go to HTTPS enabled web site.</p>

<p>You can purchase a code signing certificate from a few different sources like <a href="https://digicert.com/code-signing/" target="_blank">DigiCert</a>, who I have my code certificate from.  I am not going to cover how to purchase one.  If you do not have one, you can purchase one with DigiCert, GoDaddy, or several other companies.  Please note, they can be expensive for an individual.</p>

<h3 id="azure-key-vault">Azure Key Vault</h3>

<p><a href="https://docs.microsoft.com/en-us/azure/key-vault/key-vault-overview?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Key Vault</a> helps solve the following problems:</p>

<ul>
  <li><strong>Secrets Management</strong> - Azure Key Vault can be used to Securely store and tightly control access to tokens, passwords, certificates, API keys, and other secrets</li>
  <li><strong>Key Management</strong> - Azure Key Vault can also be used as a Key Management solution. Azure Key Vault makes it easy to create and control the encryption keys used to encrypt your data.</li>
  <li><strong>Certificate Management</strong> - Azure Key Vault is also a service that lets you easily provision, manage, and deploy public and private Transport Layer Security/Secure Sockets Layer (TLS/SSL) certificates for use with Azure and your internal connected resources.</li>
  <li><strong>Store secrets backed by Hardware Security Modules</strong> - The secrets and keys can be protected either by software or FIPS 140-2 Level 2 validated HSMs</li>
</ul>

<p>Like most services on Azure, there is a cost.  Check out the <a href="https://azure.microsoft.com/en-us/pricing/details/key-vault/?wt.mc_id=DT-MVP-4024623" target="_blank">pricing</a> for Azure Key Vault.</p>

<h2 id="getting-started">Getting Started</h2>

<p>We’ll start by creating an Azure Key Vault.</p>

<ul>
  <li>Sign in to the <a href="https://portal.azure.com#home">Azure Portal</a></li>
  <li>Click ‘Create Resource’</li>
  <li>In the ‘Search the Marketplace’ text box, type <code class="language-plaintext highlighter-rouge">Key Vault</code> and hit ‘Enter’</li>
  <li>Click ‘Create’</li>
</ul>

<p><img src="/assets/images/posts/code-sign-key-vault-create.png" alt="Setup Code Signing Certificates - Key Vault Create" class="align-center" /></p>

<h3 id="key-vault-creation">Key Vault Creation</h3>

<p>You’ll be presented with the ‘Create key vault’ wizard.  For the most part, you can leave all of the defaults.</p>

<p><img src="/assets/images/posts/code-sign-key-vault-creation.png" alt="Setup Code Signing Certificates - Key Vault Creation" class="align-center" /></p>

<p>If you want to follow along, here are the settings I used.</p>

<table>
  <thead>
    <tr>
      <th>Setting</th>
      <th>Value</th>
      <th>Comments</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Subscription</td>
      <td><em>my subscription</em></td>
      <td> </td>
    </tr>
    <tr>
      <td>Resource Group</td>
      <td><code class="language-plaintext highlighter-rouge">Presentations</code></td>
      <td>I put all of my presentation and blog resources in this one group</td>
    </tr>
    <tr>
      <td>Key Vault Name</td>
      <td><code class="language-plaintext highlighter-rouge">Code-Signing-Demo-Vault</code></td>
      <td><strong>Note</strong>: This must be unique</td>
    </tr>
    <tr>
      <td>Region</td>
      <td><code class="language-plaintext highlighter-rouge">(US) West US 2</code></td>
      <td>You should choose a region close to your resources</td>
    </tr>
    <tr>
      <td>Pricing tier</td>
      <td>Standard</td>
      <td>Where’s the <em>free</em> option :smile:</td>
    </tr>
  </tbody>
</table>

<p>The rest are defaults.</p>

<p>Click ‘Review + Create’</p>

<p>If you did everything correctly, you will get a <em>Validation passed</em>.  Validation feels good, doesn’t it!</p>

<p>Click ‘Create’</p>

<p>After a few moments, the vault will be created and you will have a page similar to this.  I blurred out the secret stuff.</p>

<p><img src="/assets/images/posts/code-sign-key-vault-overview.png" alt="Setup Code Signing Certificates - Key Vault Overview" class="align-center" /></p>

<p>Click on ‘Go to resource’</p>

<h2 id="upload-certificate">Upload Certificate</h2>

<p>There are a few ways to get a certificate into Azure Key Vault as outlined by the page <a href="https://docs.microsoft.com/en-us/azure/key-vault/create-certificate" target="_blank">Certificate creation methods</a>.  The easiest would probably be the ‘Partnered CA Providers’ method.  But who likes easy?</p>

<p>I chose to import my certificate into Azure Key Vault. This requires importing the certificate into the certificate store on my local computer first, then exporting the certificate to upload/import it into the Azure Key Vault. Since I purchased my certificate from DigiCert, I could use their <a href="https://www.digicert.com/kb/code-signing/import-export-authenticode-code-signing-certificates.htm" target="_blank">DigiCert Certificate Utility</a>. The steps below assume that you have a ‘<em>pfx</em>’ file with your private secret, aka password for the certificate.</p>

<p>Back to the Azure Key Vault page do the following:</p>

<ul>
  <li>Click on the ‘Certificates’ item in the <strong>Settings</strong> section of the table of contents</li>
  <li>Click on ‘+ Generate/Import’</li>
  <li>Under ‘Method of Certificate Creation’, chose <code class="language-plaintext highlighter-rouge">Import</code></li>
  <li>Give the certificate a name.  For me, I used the name of the certificate with the year it expires.  Example: <code class="language-plaintext highlighter-rouge">JosephGuadagno-2020</code></li>
  <li>Select the certificate file. <strong>Note</strong>: Key Vault requires that the certificate be in the <em>pfx</em> or <em>pem</em> format</li>
  <li>Use the ‘private key’ that you used when you exported your key.</li>
  <li>Click ‘Create’</li>
</ul>

<p>If everything was successful, you will see a page like this.</p>

<p><img src="/assets/images/posts/code-sign-key-vault-certificate-import-success.png" alt="Setup Code Signing Certificates - Key Vault Successfully Imported Certificate" class="align-center" /></p>

<h2 id="conclusion">Conclusion</h2>

<p>Now that the certificate is in the vault we have to give an application or user access to it.  We’ll do that in a future post.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET Core" /><category term="NuGet" /><category term="Azure" /><category term="Key Vault" /><category term="Code Signing" /><category term="Certificate" /><summary type="html"><![CDATA[Let’s take a look at how you can set up and configure Azure Key Vault to store Code Signing Certificates]]></summary></entry><entry><title type="html">Using git tag for Presentations or Workshops</title><link href="https://www.josephguadagno.net/2020/03/09/using-git-tag-for-presentations-or-workshops" rel="alternate" type="text/html" title="Using git tag for Presentations or Workshops" /><published>2020-03-09T18:30:00-07:00</published><updated>2020-03-09T18:30:00-07:00</updated><id>https://www.josephguadagno.net/2020/03/09/using-git-tag-for-presentations-or-workshops</id><content type="html" xml:base="https://www.josephguadagno.net/2020/03/09/using-git-tag-for-presentations-or-workshops"><![CDATA[<p>While we hope it never happens, demo failures happen when presenting talks or doing workshops.  I do not know of a speaker that has not had a demo go bad while up on stage or live streams. Many of us have a back up plan.  Some record screenshots or videos of the code, some create multiple copies of the code (ie: Start, Step 1, Step2, etc, End), some use assorted version control (git, TFS, SVN, etc), or even just USB drives. I, myself, have been a ‘create multiple copies of the code’ kind of presenter.  While that has worked for me in the past, it has some challenges.  The folders can be large, especially with Node projects or a lot of packages. Sharing between laptops is a pain. And backing up the solutions/demos.  After searching around for solutions for this, I decided to use git <a href="https://git-scm.com/book/en/v2/Git-Basics-Tagging" target="_blank">tag</a>ging to solve this for me.</p>

<p>In a nutshell, git tagging provides the ability to ‘tag’ a specific commit with a message similar to <a href="https://docs.microsoft.com/en-us/azure/devops/repos/tfvc/label-command-team-foundation-version-control?view=azure-devops?wt.mc_id=DT-MVP-4024623" target="_blank">label</a> in TFS. Some might be saying, but you can do the same with a branch, and while yes you can, a branch is also subjected to changes if you are not careful, a tag is associated with an individual commit id.</p>

<blockquote>
  <p><a href="https://stackoverflow.com/questions/1457103/how-is-a-tag-different-from-a-branch-in-git-which-should-i-use-here" target="_blank">How is a tag different from a branch in Git? Which should I use, here?</a></p>

  <p>-Stack Overflow</p>
</blockquote>

<h2 id="the-how">The How</h2>

<p>For my planned presentations or workshops, I generally have a script that I use. The script contains setup info for the talk (tools, software, versions, etc), prep work (how to get ready), then the actual talk.  The talk is usually bullet points with the code snippets I want to run/type/copy&amp;paste/etc.  Sometimes, as we all know, code does not always run the first time or you type something and things break and you have to recover.  The good presenters, typically recover without the attendees even knowing. This is where the <em>tagging</em> comes in. Now, instead of multiple copies of the different ‘stages’ or ‘steps’ in a demo, I commit the step and then <code class="language-plaintext highlighter-rouge">git *tag*</code> the commit.</p>

<h3 id="the-steps">The Steps</h3>

<p>First step is to start with a git repository for your presentation or demo. The provider, <a href="https://github.com/" target="_blank">GitHub</a>, <a href="https://azure.microsoft.com/en-us/products/github/?wt.mc_id=DT-MVP-4024623" target="_blank">Azure</a>, <a href="https://about.gitlab.com/" target="_blank">GitLab</a> or others does not matter. I chose GitHub because it’s free and more people use it.</p>

<p>Second step is in <code class="language-plaintext highlighter-rouge">git init</code> your folder where your code is going to be. Don’t forget to add a <code class="language-plaintext highlighter-rouge">.gitignore</code>!</p>

<p>The third step is where we ‘track’ or store our presentations ‘<em>steps</em>’. For me, I get the code to a starting point, aka ‘File New’ stage, this is where I typically start the presentation or workshop, then perform the first <strong>commit</strong>. This is a combination of 4 git commands.</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">git add .</code> - This will add all of the files since the last commit</li>
  <li><code class="language-plaintext highlighter-rouge">git commit -m "Your commit message"</code> - Replace <code class="language-plaintext highlighter-rouge">"Your commit message"</code> with a meaningful message. For me, these have been the step names, like ‘<em>Demo-Start</em>’, ‘<em>Demo-Database Complete</em>’</li>
  <li><code class="language-plaintext highlighter-rouge">git push origin master</code> - This may vary for you and is based on my understanding of git :smile:</li>
  <li><code class="language-plaintext highlighter-rouge">git tag "Tag Message/Name"</code> - Replace <code class="language-plaintext highlighter-rouge">"Tag Message/Name"</code> with the name for the tag. This is the name you want call the commit. I use the same name as the commit message.</li>
</ol>

<p>After I <code class="language-plaintext highlighter-rouge">tag</code> the commit, I am ready to repeat step 3 until all of the steps are done.</p>

<p>So an example of running through the steps would be something like this.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>gittag <span class="s2">"Demo-Start"</span>
gittag <span class="s2">"Demo-FileNewComplete"</span>
gittag <span class="s2">"Demo-DatabaseComplete"</span>
<span class="c">#...</span>
gittag <span class="s2">"Demo-Complete"</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><strong>Note</strong>: I talk about the <code class="language-plaintext highlighter-rouge">gittag</code> below.</p>

<p>The fourth step is a ‘final’ commit and tag for ‘completed demo’.</p>

<p>Step five, this is an <strong>important</strong> step.  You must commit your tags to the repo! Tags, by nature, are local to your machine.  This step is only required if you are sharing the repository, for instance, a workshop, or want the ability to ‘pull down’ the tags or steps from another machine, which is something I do :smile:.  To do that, execute <code class="language-plaintext highlighter-rouge">git push origin --tags</code> from the command line.</p>

<p>You can run the individual commands if you want but I created a bash and windows script to execute the commands for me which I included <a href="#helper-scripts">below</a> which I called <code class="language-plaintext highlighter-rouge">gittag</code>.</p>

<h2 id="reverting-back-to-a-step">Reverting Back to a Step</h2>

<p>Let’s say your in the middle of demo at step 3 and all the sudden it’s no longer working.  What do you do?  For me, I used to run back to the hotel room, cry and run through the demo a few more times to see why it didn’t work.  Well I don’t need to do that anymore, now I just execute <code class="language-plaintext highlighter-rouge">git checkout *step/tag name*</code> and the code is right where I left it.  So if I wanted to start fresh, I would run <code class="language-plaintext highlighter-rouge">git checkout Demo-Start</code> and be ready to go.</p>

<p><strong>Note</strong> If you are using a package manager like npm or NuGet, you will have to restore those packages after running this command.</p>

<p><strong>Note</strong> This does not account for any database changes, or web services call, or anything but the files on the file system that you are controlling with git.</p>

<h2 id="summary">Summary</h2>

<p>That’s it. Follow steps 1-4 <a href="#the-steps">above</a> to record/backup your steps and <a href="#reverting-back-to-a-step">revert back</a> if need be.</p>

<p>I hope this helps!</p>

<h2 id="helper-scripts">Helper Scripts</h2>

<h3 id="linux-mac-ios">Linux (Mac iOS)</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="c">#!/bin/zsh</span>
<span class="nv">commitMessage</span><span class="o">=</span><span class="nv">$1</span>
<span class="nv">commitTag</span><span class="o">=</span><span class="nv">$2</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$commitMessage</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
 </span><span class="nb">echo</span> <span class="s2">"syntax: gittag 'message'"</span>
 <span class="nb">exit</span><span class="p">;</span>
<span class="k">fi
if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$commitTag</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
 <span class="nv">$commitTag</span><span class="o">=</span><span class="nv">$commitMessage</span>
<span class="k">fi
</span>git add <span class="nb">.</span>
git commit <span class="nt">-m</span> <span class="nv">$commitMessage</span>
git push origin master
git tag <span class="nv">$commitTag</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="windows">Windows</h3>

<div class="language-batch highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre>@ECHO <span class="kd">OFF</span>
<span class="k">if</span> <span class="s2">"</span><span class="err">%</span><span class="s2">1"</span><span class="o">==</span><span class="s2">""</span> <span class="kd">GOTO</span> <span class="kd">MissingArg1</span>
<span class="kd">SET</span> <span class="kd">commitMessage</span><span class="o">=</span><span class="err">%</span><span class="m">1</span>

<span class="k">if</span> <span class="s2">"</span><span class="err">%</span><span class="s2">2"</span><span class="o">==</span><span class="s2">""</span> <span class="o">(</span><span class="kd">SET</span> <span class="kd">commitTag</span><span class="o">=</span><span class="nv">%commitMessage%</span><span class="o">)</span> <span class="k">else</span> <span class="o">(</span><span class="kd">SET</span> <span class="kd">commitTag</span><span class="o">=</span><span class="err">%</span><span class="m">2</span><span class="o">)</span>

<span class="kd">git</span> <span class="kd">add</span> .
<span class="kd">git</span> <span class="kd">commit</span> <span class="na">-m </span><span class="nv">%commitMessage%</span>
<span class="kd">git</span> <span class="kd">push</span> <span class="kd">origin</span> <span class="kd">master</span>
<span class="kd">git</span> <span class="kd">tag</span> <span class="nv">%commitTag%</span>
<span class="k">exit</span> <span class="m">0</span>

<span class="nl">:MissingArg</span><span class="m">1</span>
<span class="nb">echo</span> <span class="kd">syntax</span>: <span class="kd">gittag</span> <span class="s1">'message'</span> <span class="s1">'tag'</span>
<span class="nb">echo</span>   <span class="kd">tag</span> <span class="kd">is</span> <span class="kd">optional</span>
<span class="k">exit</span> <span class="m">1</span>
</pre></td></tr></tbody></table></code></pre></div></div>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="git" /><category term="Presentation" /><category term="Presentations" /><category term="Speaking" /><category term="workshop" /><summary type="html"><![CDATA[While we hope it never happens, demo failures happen when presenting talks or doing workshops. I do not know of a speaker that has not had a demo go bad while up on stage or live streams. Many of us have a back up plan... After searching around for solutions for this, I decided to use git tagging to solve this for me.]]></summary></entry><entry><title type="html">Visual Studio Settings for Presentations</title><link href="https://www.josephguadagno.net/2020/01/11/visual-studio-settings-for-presentations" rel="alternate" type="text/html" title="Visual Studio Settings for Presentations" /><published>2020-01-11T07:30:00-07:00</published><updated>2020-01-11T07:30:00-07:00</updated><id>https://www.josephguadagno.net/2020/01/11/visual-studio-settings-for-presentations</id><content type="html" xml:base="https://www.josephguadagno.net/2020/01/11/visual-studio-settings-for-presentations"><![CDATA[<p>There was a twitter conversation, started by <a href="https://twitter.com/julielerman?s=20" target="_blank">@julielerman</a> around building an extension would quickly disable the <strong>Quick Info</strong> feature for C# to make it easier for presenters/twitchers and others when recording demos or showing code.</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Quick Info in <a href="https://twitter.com/VisualStudio?ref_src=twsrc%5Etfw">@visualstudio</a> is *slaying* me. Methods with really really long descriptions cover up code you try to read add to. I&#39;ve found many complaints about it by googling but for C# , no way to disable it. Oddly, you can only disable it for C++. Help?</p>&mdash; Julie Lerman (@julielerman) <a href="https://twitter.com/julielerman/status/1206932076940972032?ref_src=twsrc%5Etfw">December 17, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>It quickly changed to building an extension to quickly switch between settings in Visual Studio.</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">👍 In general I&#39;d love to see a &quot;Presentation mode&quot; that sets a set of settings like fontsize, disables quick info etc, and is quickly undone again once you&#39;re done presenting.</p>&mdash; .Morten (@dotMorten) <a href="https://twitter.com/dotMorten/status/1207151902074425345?ref_src=twsrc%5Etfw">December 18, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>I chimed in a couple of times about how I accomplish this without a Visual Studio extension so I decided to blog about it.  Luckily, the master of Visual Studio extensions, <a href="https://twitter.com/mkristensen" target="_blank">@mkristensen</a>, and Program Manager for Visual Studio Extensibility, chimed in that he has a solution in mind to make it easier.  In either case, let’s get to it.</p>

<h2 id="tools-needed">Tools Needed</h2>

<p>The list is simple Visual Studio (any edition) and something that syncs folders/drives (optional).  I use <a href="https://onedrive.live.com?invref=406ee4c95978c038&amp;invscr=90" target="_blank">OneDrive</a> for synchronizing/backing up key files but you can use whatever is easier for you.</p>

<h2 id="the-process">The Process</h2>

<h3 id="step-1-save-your-default-settings">Step 1: Save your ‘Default’ settings</h3>

<p>If you are unsure how to save your settings, check out <a href="https://docs.microsoft.com/en-us/visualstudio/ide/environment-settings?view=vs-2019&amp;?wt.mc_id=DT-MVP-4024623" target="_blank">Environment Setting for Visual Studio</a> in the MSDN docs. Feel free to call the settings whatever you wish, I use ‘<em>default</em>’ because these are my day to day, or week to week settings lately :smile:, for Visual Studio.</p>

<h3 id="step-1a-save-your-files-to-onedrive">Step 1a: Save your files to OneDrive</h3>

<p>Again, you can save these to where ever you want, I chose OneDrive because a have a few different machines I use for presentations, both work and personal, but I like my settings to be the same. So save off your ‘<em>default</em>’ settings to a OneDrive folder.  Mine are saved off to <a href="https://1drv.ms/u/s!AjjAeFnJ5G5AhKAMUjfhuvgrMKMDkA?e=gqYODh" target="_blank">Presentations\Settings</a>.</p>

<h3 id="step-2a-create-your-presentation-settings">Step 2a: Create your Presentation settings</h3>

<p>There are probably 3,000,000 opinions on what makes a good set of settings for presentations.  There are a couple of things I change with mine, the Font, the Font Size, and the Current Line.</p>

<p>For the font, I was using <a href="https://github.com/adobe-fonts/source-code-pro" target="_blank">Source Code Pro</a> for a while. Lately, I have been switching between Monaco and Menlo. Pick whatever font you like. There is a pretty good blog post that author shows their <a href="https://itnext.io/11-best-programming-fonts-724283a9ed57" target="_blank">Top 11 Programming Fonts</a> for editing source code.</p>

<p>For the font size, I chose <em>16</em>, which is normally good for most screens.</p>

<p>I also change the <strong>Current Statement</strong> display item.  I modify the <em>item background</em> to <em>yellow</em> so that it is easier to follow along.</p>

<p>While most of us only change the fonts, colors, etc for the <em>Text Editor</em>, depending on what you are showing, you might want to change others like <em>Editor Tooltip</em>, <em>Immediate Window</em>, <em>Output Window</em>, <em>Data Tips</em>, and more. Take a look at the <strong>Show setting for:</strong> drop down for more options. You can see the settings I use for presentations in this <a href="https://1drv.ms/u/s!AjjAeFnJ5G5AhKAMUjfhuvgrMKMDkA?e=gqYODh" target="_blank">folder</a> or directly ‘<a href="https://1drv.ms/u/s!AjjAeFnJ5G5Ag-Qb_N_MaeQ1671klw?e=dNbv1H" target="_blank">presentations.vssettings</a>’</p>

<h3 id="step-2b-save-your-presentation-settings">Step 2b: Save your Presentation settings</h3>

<p>Now save your new presentation settings to the same folder as before.  This is not required. It just makes it easier to find the settings later.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Now you have two different settings that you can load based on your needs at the time.  To switch between them import the setting you want. Just be sure to chose <strong>No, just import new settings, overwriting my current settings</strong> when you do.</p>

<p>Hopefully Mads and team come up with a solution to make it a little bit easier to swap between settings for those of us that do it a little more often.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Visual Studio" /><category term="Settings" /><category term="Presentation" /><category term="Presentations" /><category term="presenting" /><summary type="html"><![CDATA[There was a twitter conversation around building an extension to make it easier for presenters/twitchers and others to quickly switch between settings in Visual Studio. I decided to blog about how I do it.]]></summary></entry><entry><title type="html">New Type of Identity Theft</title><link href="https://www.josephguadagno.net/2019/09/24/new-type-of-identity-theft" rel="alternate" type="text/html" title="New Type of Identity Theft" /><published>2019-09-24T12:30:00-07:00</published><updated>2019-09-24T12:30:00-07:00</updated><id>https://www.josephguadagno.net/2019/09/24/new-type-of-identity-theft</id><content type="html" xml:base="https://www.josephguadagno.net/2019/09/24/new-type-of-identity-theft"><![CDATA[<p>Or at least new to me…</p>

<p>A few days ago, I got an email with the subject of <em>Inquiry</em> from a <em>Michael Fleming</em> at <em>michaeltfleming6@gmail.com</em>.  Initially, I didn’t think anything of it.  This time of the year I get a lot of emails with random subjects because of <a href="https://www.desertcodecamp.com/home" target="_blank">Desert Code Camp</a>. So I opened it and here is what it said.</p>

<blockquote>
  <p>Joseph,</p>

  <p>Good afternoon.  I was wondering if you could tell me which city you lived in 2018 before moving back to <strong><em>city removed</em></strong>?</p>

  <p><strong><em>city name 1 removed</em></strong></p>

  <p><strong><em>city name 2 removed</em></strong></p>

  <p><strong><em>city name 3 removed</em></strong></p>

  <p>I am doing an internet recruiting quiz and this popped up as a question.  Hoping you can help me out!  I would really appreciate it.  : )</p>

  <p>Mike</p>
</blockquote>

<p>This seems a bit odd especially for a “<strong>internet recruiting quiz</strong>”. So I responded to the email with.</p>

<blockquote>
  <p>Why would were I live pop up in a recruiting quiz?</p>
</blockquote>

<p>“Mike” responded about 13 minutes later with…</p>

<blockquote>
  <p>I apologize for bothering you.  If you can’t tell me that’s fine.  I know this seems super random, but the quiz grabs all kinds of random stuff from material/content published on the internet.</p>
</blockquote>

<p>To me, this was even odder.  Although the response time made it seem real the response really didn’t.</p>

<p>To which I responded…</p>

<blockquote>
  <p>Honestly it sounds like you are attempting to steal my identity with a question like that.</p>
</blockquote>

<p>“Mike” then responded 4 minutes later with…</p>

<blockquote>
  <p>Yeah.  I kind of thought that after I sent the message.  I apologize.  Just disregard it all.  I do random stuff sometimes and realized, after the fact, that it probably wasn’t appropriate.  Enjoy the rest of your evening.</p>
</blockquote>

<p>That pretty much ended the conversation.  Maybe I am overly cautious or maybe this was honest.  I did move, I am in Technology but a lot of this stuff is publicly available.</p>

<p class="notice--warning">Moral of the story, be careful of what you share and to whom you share it with.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Identity Theft" /><category term="Security" /><category term="Technology" /><summary type="html"><![CDATA[A few days ago, I got an email with the subject of *Inquiry* from a *Michael Fleming* at *michaeltfleming6@gmail.com*. Initially, I didn't think anything of it. This time of the year I get a lot of emails with random subjects because of [Desert Code Camp](https://www.desertcodecamp.com/home). So I opened it and here is what it said.]]></summary></entry><entry><title type="html">Dynamic Blog Posts with Jekyll</title><link href="https://www.josephguadagno.net/2019/08/12/dynamic-blog-posts-with-jekyll" rel="alternate" type="text/html" title="Dynamic Blog Posts with Jekyll" /><published>2019-08-12T06:30:00-07:00</published><updated>2019-08-12T06:30:00-07:00</updated><id>https://www.josephguadagno.net/2019/08/12/dynamic-blog-posts-with-jekyll</id><content type="html" xml:base="https://www.josephguadagno.net/2019/08/12/dynamic-blog-posts-with-jekyll"><![CDATA[<p>Well “dynamic-ish”. The <a href="/speaking-engagements/">Speaking Engagements</a> page on this site uses <a href="https://jekyllrb.com/docs/datafiles/" target="_blank">data files</a> to generate the list of upcoming and previous speaking engagements. Which generates the content shown here.</p>

<p><img src="/assets/images/posts/data-files-speaking-engagements.png" alt="image-center" title="Speaking Engagements" class="align-center" /></p>

<p>Setting up the data files and corresponding markdown files are pretty easy.  First, let’s get started with the data file. Jekyll supports loading data from <a href="https://yaml.org" target="_blank">YAML</a>, <a href="https://www.json.org" target="_blank">JSON</a>, <a href="https://en.wikipedia.org/wiki/Comma-separated_values" target="_blank">CSV</a>, and <a href="https://en.wikipedia.org/wiki/Tab-separated_values" target="_blank">TSV</a> files located in the <code class="language-plaintext highlighter-rouge">_data</code> directory. <strong>Note</strong>, if you use a CSV or TSV files, there must be a header row in the file.  I used a Json file for this examples and named it <code class="language-plaintext highlighter-rouge">engagements.json</code>. You can view the full <a href="https://github.com/jguadagno/jguadagno.github.io/blob/master/_data/engagements.json" target="_blank">engagements.json</a> on Github. Below is a excerpt of the file for sake a brevity.</p>

<h3 id="engagementsjson">Engagements.json</h3>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="nl">"eventName"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Southeast Valley .NET User Group"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"eventUrl"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://www.meetup.com/sevdnug/events/263472581"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"location"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Chandler, AZ "</span><span class="p">,</span><span class="w">
        </span><span class="nl">"presentation"</span><span class="w"> </span><span class="p">:</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Look into your Application with Azure Application Insights"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/presentations/look-into-your-application-with-application-insights"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"date"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2019-08-22"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"comments"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"isCurrent"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="w">
        </span><span class="nl">"eventName"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Northwest Valley .NET User Group"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"eventUrl"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"https://www.meetup.com/NWVDNUG/events/263472395/"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"location"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Glendale, AZ "</span><span class="p">,</span><span class="w">
        </span><span class="nl">"presentation"</span><span class="w"> </span><span class="p">:</span><span class="w">
        </span><span class="p">{</span><span class="w">
            </span><span class="nl">"name"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"Look into your Application with Azure Application Insights"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"/presentations/look-into-your-application-with-application-insights"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"date"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="s2">"2019-08-21"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"comments"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
        </span><span class="p">},</span><span class="w">
        </span><span class="nl">"isCurrent"</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
    </span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>There is nothing special with this json file. The only “<em>requirement</em>”, is that the json file is an array of objects. All of the properties can be in the root object (lines 2-14). However, I chose to break up the object a bit more and add a child property of the engagement called <code class="language-plaintext highlighter-rouge">presentation</code>, which has 4 properties: <code class="language-plaintext highlighter-rouge">name</code>, <code class="language-plaintext highlighter-rouge">url</code>, <code class="language-plaintext highlighter-rouge">date</code>, and <code class="language-plaintext highlighter-rouge">comments</code>.</p>

<p>Let’s take a look at how we use this data file to make the <a href="/speaking-engagements/">Speaking Engagements</a> page “dynamic”. While technically it is not dynamic, I only need to edit and commit the file and Jekyll will generate a new page.</p>

<h3 id="speaking-engagementsmd">Speaking-Engagements.md</h3>

<p>Once you create the data file and place it in the <code class="language-plaintext highlighter-rouge">_data</code> directory, Jekyll makes it available on the <code class="language-plaintext highlighter-rouge">site.data</code> tag in your site markdown.  Since our file was named <code class="language-plaintext highlighter-rouge">engagements</code> we can access it like so, <code class="language-plaintext highlighter-rouge">site.data.engagements</code>. This will be an array of <code class="language-plaintext highlighter-rouge">engagement</code> objects.</p>

<p>Since I wanted to manipulate the lists of engagements, separate current engagements from past engagements and do some sorting, I created a variable, <code class="language-plaintext highlighter-rouge">sortedCurrentEngagements</code>.  This variable will hold a sorted, by <code class="language-plaintext highlighter-rouge">presentation.date</code>, list of engagements where the <code class="language-plaintext highlighter-rouge">isCurrent</code> property is <code class="language-plaintext highlighter-rouge">true</code>. The syntax for the assign is:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>
{% assign sortedCurrentEngagements = site.data.engagements | sort: 'presentation.date' | where:'isCurrent', 'true' %}

</pre></td></tr></tbody></table></code></pre></div></div>

<p>The next step is to iterate/loop through all of the engagements in the <code class="language-plaintext highlighter-rouge">sortedCurrentEngagements</code>.  This is where the flexibility of Jekyll and the <a href="https://shopify.github.io/liquid/" target="_blank">Liquid</a> template engine come to play. We’ll use the <a href="https://shopify.github.io/liquid/tags/iteration/" target="_blank">Iteration</a> tag within the Liquid templates to iterate through the engagements.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>
{% for engagement in sortedCurrentEngagements -%}
|<span class="p">[</span><span class="nv">{{engagement.eventName}}</span><span class="p">](</span><span class="sx">{{engagement.eventUrl}}</span><span class="p">)</span>{:target="_blank"}|{{engagement.location}}|<span class="p">[</span><span class="nv">{{engagement.presentation.name}}</span><span class="p">](</span><span class="sx">{{engagement.presentation.url}}</span><span class="p">)</span>|{{engagement.presentation.date}}|{{engagement.presentation.comments }}|
{% endfor %}

</pre></td></tr></tbody></table></code></pre></div></div>

<p>The code is a little messy because I have it inside a table. Line 1 and 3, performs the iteration. Line 2 is where we write out all of the data want to. As you can see, to access any of the properties, you need wrap them in double curly braces <code class="language-plaintext highlighter-rouge">{{}}</code></p>

<p>For the previous engagements, we just create another variable, similar to <code class="language-plaintext highlighter-rouge">sortedCurrentEngagements</code> but set <code class="language-plaintext highlighter-rouge">isCurrent</code> to <code class="language-plaintext highlighter-rouge">false</code>.</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre>
{% assign sortedPastEngagements = site.data.engagements | sort: 'presentation.date' | reverse | where:'isCurrent', 'false' %}

</pre></td></tr></tbody></table></code></pre></div></div>

<p>You can see the entire <a href="https://github.com/jguadagno/jguadagno.github.io/blob/master/_pages/speaking-engagements.md" target="_blank">speaking-engagements.md</a> file on Github.</p>

<p>For more info on this, check out the <a href="https://jekyllrb.com/docs/datafiles/" target="_blank">data files</a> documentation on the <a href="https://jekyllrb.com/" target="_blank">Jekyll</a> <a href="https://jekyllrb.com/docs/" target="_blank">docs</a> site.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Jekyll" /><summary type="html"><![CDATA[Well “dynamic-ish”. The Speaking Engagements page on this site uses data files to generate the list of upcoming and previous speaking engagements. Which generates the content shown here.]]></summary></entry><entry><title type="html">Congressional App Challenge</title><link href="https://www.josephguadagno.net/2019/07/08/congressional-app-challenge" rel="alternate" type="text/html" title="Congressional App Challenge" /><published>2019-07-08T05:30:00-07:00</published><updated>2019-07-08T05:30:00-07:00</updated><id>https://www.josephguadagno.net/2019/07/08/congressional-app-challenge</id><content type="html" xml:base="https://www.josephguadagno.net/2019/07/08/congressional-app-challenge"><![CDATA[<p>I was proud to participate in the Congressional App Challenge last year even though it was just to help promote it in Arizona. The Congressional App Challenge is a Congressional initiative to encourage student engagement in STEM and, more specifically, computer science. Middle and high school students from across America are invited to participate.</p>

<p>They are looking for students to <a href="https://www.congressionalappchallenge.us/students/student-registration/?mc_cid=6aec36a431&amp;mc_eid=52a71260c4" target="_blank">participate</a> and companies to help <a href="https://www.congressionalappchallenge.us/sponsors/?mc_cid=6aec36a431&amp;mc_eid=52a71260c4" target="_blank">sponsor</a>.</p>

<p>I was lucky enough to get invited to the finals presentation in Washington, DC for the last one and was very impressed with what these students created.  If you have a student or students that are interested in technology, I urge you to take a look and encourage them to apply for the competition.  If you are company that is looking to help grow the interest in technology, believe me we do, please visit the <a href="https://www.congressionalappchallenge.us/sponsors/?mc_cid=6aec36a431&amp;mc_eid=52a71260c4" target="_blank">sponsor</a> section of their site.</p>

<p>Details on the program are below</p>

<h2 id="congressional-app-challenge">Congressional App Challenge</h2>

<p><img src="/assets/images/posts/congressional-app-challenge.jpg" alt="image-center" title="Congressional App Challenge" class="align-center" /></p>

<p>The annual Congressional App Challenge (CAC) launches today (7/8/2019). The CAC is a Congressional initiative to encourage student engagement in STEM and, more specifically, computer science. Middle and high school students from across America are invited to participate. <a href="https://www.congressionalappchallenge.us/students/student-registration/?mc_cid=6aec36a431&amp;mc_eid=52a71260c4" target="_blank">Student registration opens today</a>!</p>

<p>Over <a href="https://www.congressionalappchallenge.us/students/participating-districts/?mc_cid=6aec36a431&amp;mc_eid=52a71260c4" target="_blank">250 members of Congress</a> have registered to host a challenge for their district’s students. “The Congressional App Challenge is the largest series of student coding competitions in the world,” said Tim Lordan, Executive Director of the Internet Education Foundation, which was appointed as the CAC’s official sponsor by the U.S. House Of Representatives.</p>

<p>The CAC was created because <strong>Congress recognizes how essential computer science and STEM skills are for economic growth and innovation</strong>, and that the U.S. is currently experiencing a dearth of adequately trained technical talent. By some estimates, there are nearly a quarter of a million unfilled programming jobs in the US. The CAC is a congressional effort to maintain American competitiveness by proactively inspiring our youth and encouraging them to pursue these crucial skills.</p>

<p>The Congressional App Challenge winners receive <strong>official recognition from their Member of Congress, and their app is displayed at the U.S. Capitol</strong> for one year.</p>

<p><img src="/assets/images/posts/congressional-app-challenge-diverse.png" alt="image-center" title="Congressional App Challenge Diversity" class="align-center" /></p>

<p>The CAC aims to bridge the <strong>gender</strong>, <strong>geographic</strong>, and <strong>racial</strong> gaps in tech by building the domestic pipeline of future tech innovators. The program has inspired over <strong>14,000</strong> students across <strong>49 states</strong> and territories to code over <strong>4,000</strong> apps for desktop PCs, web, tablets, mobiles or other platforms. Participant demographics for the <strong>Congressional App Challenge</strong> surpass all tech industry diversity metrics. This year, the Congressional App Challenge strives to build upon this success.</p>

<p>With the support of the sponsors above, the Congressional App Challenge has encouraged thousands of students to ignite their enthusiasm for computer science education. Interested in joining these leaders The Congressional App Challenge has <a href="https://www.congressionalappchallenge.us/sponsors/?mc_cid=6aec36a431&amp;mc_eid=52a71260c4" target="_blank">sponsorship options for organizations of any size</a>.</p>

<h3 id="follow-them-on-social-media">Follow Them on Social Media</h3>

<ul>
  <li>Facebook: <a href="https://www.facebook.com/CongressionalAppChallenge?mc_cid=6aec36a431&amp;mc_eid=52a71260c4" target="_blank">@CongressionalAppChallenge</a></li>
  <li>Twitter: <a href="https://twitter.com/CongressionalAC">@CongressionalAC</a></li>
  <li>Facebook: <a href="https://www.facebook.com/CongressionalAppChallenge?mc_cid=6aec36a431&amp;mc_eid=52a71260c4" target="_blank">@CongressionalAppChallenge</a></li>
  <li>Instagram: <a href="https://www.instagram.com/congressionalappchallenge/?mc_cid=6aec36a431&amp;mc_eid=52a71260c4" target="_blank">congressionalappchallenge</a></li>
  <li>Hashtag: #Congress4CS</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Archive" /><category term="stem" /><category term="Community" /><category term="government" /><summary type="html"><![CDATA[I was proud to participate in the Congressional App Challenge last year even though it was just to help promote it in Arizona. The Congressional App Challenge is a Congressional initiative to encourage student engagement in STEM and, more specifically, computer science. Middle and high school students from across America are invited to participate.]]></summary></entry><entry><title type="html">Upcoming Events in Phoenix For April 2019</title><link href="https://www.josephguadagno.net/2019/04/24/upcoming-events-in-phoenix-for-april-2019" rel="alternate" type="text/html" title="Upcoming Events in Phoenix For April 2019" /><published>2019-04-24T06:30:00-07:00</published><updated>2019-04-24T06:30:00-07:00</updated><id>https://www.josephguadagno.net/2019/04/24/upcoming-events-in-phoenix-for-april-2019</id><content type="html" xml:base="https://www.josephguadagno.net/2019/04/24/upcoming-events-in-phoenix-for-april-2019"><![CDATA[<p>We have two great technology events taking place in the Phoenix metro area over the next two weeks, <a href="https://www.meetup.com/Phoenix-Azure-User-Group/events/260838019" target="_blank">Global Azure Boot Camp</a> and <a href="https://www.meetup.com/Arizona-SQL-Server-User-Group/events/260081750&gt;" target="_blank">SQL Saturday</a>.</p>

<h2 id="global-azure-boot-camp">Global Azure Boot Camp</h2>

<p>Taking place on April 27th, in Chandler, AZ and all over the world, communities will come together once again in the seventh great Global Azure Boot Camp event! We are one of many user groups organizing our own one-day deep dive class on Azure the way it works best for our Phoenix members. The result is that thousands of people get to learn about Azure and join together online under the social hashtag #GlobalAzure!</p>

<p>More details can found at <a href="https://www.meetup.com/Phoenix-Azure-User-Group/events/260838019" target="_blank">Global Azure Boot Camp</a></p>

<h2 id="sql-saturday">SQL Saturday</h2>

<p>This is our big event of the year! SQLSaturday is a FREE training event for Microsoft Data Platform professionals and those wanting to learn about SQL Server, Business Intelligence and Analytics. There are over 40 sessions across 7 tracks with speakers from all over the country. If the learning and networking are not enough, there’ll also be raffle prizes!</p>

<p><strong><em>Please ensure you register for the event at this link</em></strong></p>

<p>Register at <a href="https://www.sqlsaturday.com/869/" target="_blank">https://www.sqlsaturday.com/869/</a></p>

<p>For details, visit there meetup site at <a href="https://www.meetup.com/Arizona-SQL-Server-User-Group/events/260081750" target="_blank">SQL Saturday</a>.</p>

<h2 id="desert-code-camp">Desert Code Camp</h2>

<p>Oh, if you are wondering when the next Desert Code Camp, I’m working on getting a date from the venue. Stayed tuned.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Archive" /><category term="Arizona" /><category term="Events" /><category term="Community" /><summary type="html"><![CDATA[We have two great technology events taking place in the Phoenix metro area over the next two weeks, Global Azure Boot Camp and SQL Saturday.]]></summary></entry><entry><title type="html">I’m Running for the Board of the DotNet Foundation</title><link href="https://www.josephguadagno.net/2019/03/19/im-running-for-the-board-of-the-dotnet-foundation" rel="alternate" type="text/html" title="I’m Running for the Board of the DotNet Foundation" /><published>2019-03-19T14:35:20-07:00</published><updated>2019-03-19T14:35:20-07:00</updated><id>https://www.josephguadagno.net/2019/03/19/im-running-for-the-board-of-the-dotnet-foundation</id><content type="html" xml:base="https://www.josephguadagno.net/2019/03/19/im-running-for-the-board-of-the-dotnet-foundation"><![CDATA[<h2 id="about-me">About Me</h2>

<p>I am a Microsoft Developer Service MVP. I have been an MVP since 2008. I have spoken at multiple conferences throughout the last ten years.</p>

<p><img src="https://avatars3.githubusercontent.com/u/3209610?s=250" alt="alt text" title="Joseph Guadagno" /></p>

<h2 id="why-im-running">Why I’m Running</h2>

<p>For 20 years or so I have been in Software Development. During that time I have used many tools, languages, and technologies. I started out programming with a small book on QuickBASIC. I later moved on to Visual Basic for DOS. Windows then came along and I starting using Visual Basic for Windows, I then migrated to Visual Basic .NET and eventually ended up using Visual C#. I work as an Engineering Director at Rocket Mortgage, based in Detroit, MI. I am a public speaker and present internationally on a lot of different <a href="https://www.josephguadagno.net/presentations/" target="_blank">technology topics</a>.</p>

<p>One of my primary goals in life is to <em>leave this live (planet) better than it was when I joined it</em>. I do that in many ways. My favorite way is to grow people and help make them better. I do this through education. Presenting/Public Speaking.  I also do this by helping others find ways to help themselves grow.  I believe that being on the board of the .NET Foundation, I can help the vision of .NET and make it easier for others to adopt and grow themselves.  In the past, I have worked with many other talented professionals while serving on the Board of Directors for <a href="#ineta-experience" target="_blank">INETA</a> to help spread the education and adoption of .NET.</p>

<p>I feel I am very well suited for the position.</p>

<h2 id="my-community-contributions">My Community Contributions</h2>

<h3 id="net-contributions">.NET Contributions</h3>

<ul>
  <li><a href="https://github.com/jguadagno/azure-docs">Azure Documentation</a></li>
  <li><a href="https://github.com/jguadagno/azure-content">Azure Content</a></li>
</ul>

<h3 id="ineta-experience">INETA Experience</h3>

<p>INETA was the International .NET Association.  I served on the board for eight years.  Two as Director of Marketing, four as President, and two as Vice President.</p>

<h3 id="community-contributions">Community Contributions</h3>

<ul>
  <li><a href="https://www.desertcodecamp.com" target="_blank">Organizer of Desert Code Camp</a></li>
  <li><a href="https://www.AZGiveCamp.org" target="_blank">Contributes to AZ Give Camp</a></li>
</ul>

<h3 id="open-source-projects">Open Source Projects</h3>

<ul>
  <li><a href="https://bingsharp.codeplex.com/" target="_blank">BingSharp</a>: A wrapper around the Bing API in C#</li>
  <li><a href="https://github.com/jguadagno/bootstrapThemeSwitcher" target="_blank">bootstrapThemeSwitcher</a>: jquery plugin to dynamically load Bootstrap themes</li>
  <li><a href="https://github.com/jguadagno/twbs-flickrCarousel" target="_blank">twbs-flickrCarousel</a>: a bootstrap carousel to loop through Flickr pictures</li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.Extensions" target="_blank">JosephGuadagno.Extensions</a></li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.Utilities" target="_blank">JosephGuadagno.Utilities</a></li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.Extensions.EF" target="_blank">JosephGuadagno.Extensions.EF</a></li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.Extensions.Web" target="_blank">JosephGuadagno.Extensions.Web</a></li>
  <li><a href="https://github.com/jguadagno/JosephGuadagno.AzureHelpers" target="_blank">JosephGuadagno.AzureHelpers</a></li>
</ul>

<h3 id="others">Others</h3>

<ul>
  <li>Public Speaker</li>
  <li>Organized Events at the Microsoft MVP Summit</li>
  <li>Book Reviewer</li>
</ul>

<h2 id="links">Links</h2>

<ul>
  <li><a href="https://www.josephguadagno.net" target="_blank">My blog</a></li>
  <li><a href="https://www.facebook.com/JosephGuadagnoNet/" target="_blank">Facebook Speaking Page</a></li>
</ul>

<h2 id="contact-information">Contact Information</h2>

<ul>
  <li>Twitter: <a href="https://twitter.com/jguadagno" target="_blank">@jguadagno</a></li>
  <li>GitHub: <a href="https://github.com/jguadagno" target="_blank">@jguadagno</a></li>
  <li><a href="https://jjg.me/MVPLink" target="_blank">Microsoft MVP Profile</a></li>
  <li><a href="https://www.linkedin.com/in/josephguadagno/" target="_blank">LinkedIn</a></li>
  <li><a href="https://stackoverflow.com/users/89184/joseph-guadagno" target="_blank">StackOverflow</a></li>
  <li>Email: <a href="mailto:jguadagno@hotmail.com" target="_blank">jguadagno@hotmail.com</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="News" /><category term="Archive" /><category term=".NET" /><category term=".NET Core" /><category term="dotnet" /><category term="Community" /><summary type="html"><![CDATA[About Me]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://avatars3.githubusercontent.com/u/3209610?s=250" /><media:content medium="image" url="https://avatars3.githubusercontent.com/u/3209610?s=250" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Im Speaking on a Boat</title><link href="https://www.josephguadagno.net/2017/12/04/im-speaking-on-a-boat/" rel="alternate" type="text/html" title="Im Speaking on a Boat" /><published>2017-12-04T02:30:24-07:00</published><updated>2017-12-04T02:30:24-07:00</updated><id>https://www.josephguadagno.net/2017/12/04/im-speaking-on-a-boat</id><content type="html" xml:base="https://www.josephguadagno.net/2017/12/04/im-speaking-on-a-boat/"><![CDATA[<p>Well, it’s more than a boat is a cruise ship, the <a href="https://ncl.com" target="_blank">Norwegian Cruise Lines</a> <a href="https://www.ncl.com/cruise-ship/escape" target="_blank">Escape</a>. We will be departing from Miami with port stops in Falmouth (Jamaica); George Town (Grand Cayman); Great Stirrup Cay (Bahamas); Nassau (Bahamas); and come back to Miami.</p>

<p><a href="https://www.chriswoodruff.com/blog" target="_blank">Chris Woodruff</a> and I will be providing 2 and half days of instruction on .NET Core at <a href="https://www.techoutbound.com/index.html" target="_blank">TechOutbound</a>. It’ll be a combination of instruction and hands on workshops. Here’s the topics we will be covering.</p>

<ul>
  <li>C# 7</li>
  <li>Entity Framework Core 2.0</li>
  <li>ASP.NET Core MVC</li>
  <li>ASP.NET Core Web API</li>
  <li>Client-side Development with ASP.NET Core</li>
  <li>Security and Identity</li>
  <li>Testing</li>
  <li>Debugging and Performance</li>
  <li>Hosting and Deployment</li>
  <li>Migrating from.NET Framework</li>
  <li>Docker and Containers</li>
  <li>ASP.NET Core and the Cloud</li>
</ul>

<p>Register now by following the instructions and use code <strong>BFFJG</strong> when you do for an additional $100 off your registration</p>]]></content><author><name>Joseph Guadagno</name></author><category term="News" /><category term=".NET Core" /><category term="ASP.NET" /><category term="Speaking" /><category term="Web" /><category term="Azure" /><category term="Visual Studio" /><summary type="html"><![CDATA[Well, it’s more than a boat is a cruise ship, the Norwegian Cruise Lines Escape. We will be departing from Miami with port stops in Falmouth (Jamaica); George Town (Grand Cayman); Great Stirrup Cay (Bahamas); Nassau (Bahamas); and come back to Miami.]]></summary></entry><entry><title type="html">Why I Organize Desert Code Camp</title><link href="https://www.josephguadagno.net/2017/10/21/why-i-organize-desert-code-camp/" rel="alternate" type="text/html" title="Why I Organize Desert Code Camp" /><published>2017-10-21T06:53:08-07:00</published><updated>2017-10-21T06:53:08-07:00</updated><id>https://www.josephguadagno.net/2017/10/21/why-i-organize-desert-code-camp</id><content type="html" xml:base="https://www.josephguadagno.net/2017/10/21/why-i-organize-desert-code-camp/"><![CDATA[<p>While I was mingling at the Desert Code Camp after party, I was talking to a long time attendee, Liz. Liz makes the drive up from Tucson every year to attend the event. Liz and I were chatting for a bit when she told me how much she loved Desert Code Camp and how much she has learned from it. She also told me a few tips and tricks that she learned over the years to be about to handle all of the learning that happens. She was kind enough to share this with me so I will share it with you.</p>

<blockquote>
  <p>I learned some things about how to benefit myself best from DCC through the years. I used to go to all the sessions of which I knew nothing about and try to get some basic understanding. As it turned out, I usually felt a bit defeated because it was a lot more complicated than I anticipated, and required more fundamental understanding and skills than I had. The day, although enjoyable, would be highly arduous cognitively speaking. I would be very tired at the end and had a hard time even paying attention. This year, I finally did it right. I previewed the web site as often as time would allow and took notes on technologies listed. I perused them and let my mind wander to how I could use this and what I would want to learn from this. I began an interactive relationship with the subject before the actual camp. I picked sessions of which I had at least a little bit of understanding initially or picked sessions with which I could do a little bit of research beforehand to prime my brain. I also know that after the wonderful traditional pizza and pasta lunch, I get a bit sleepy. Not only did I work a little nap in on lunch hour, but I deliberately picked afternoon sessions which would be lighter as far as subject matter or presenter charisma so that I would be more engaged. As it turned out, this conference has been the best for me so far. Thanks to Joe Guadagno, Gilbert Chandler Community College, the sponsors and the volunteers for bringing together a fantastic camp.</p>
</blockquote>

<p>People ask me all the time why do I organize Desert Code Camp every year? Why do I spend an average of 480 a year putting it together? This is the reason. I am able to help people and make an impact on their lives. One of my life mantras is “<em><strong>Leave this world better than it was when you came into it</strong></em>”. I hope you can make an impact too.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Desert Code Camp" /><category term="Community" /><category term="Speaking" /><category term="Arizona" /><summary type="html"><![CDATA[While I was mingling at the Desert Code Camp after party, I was talking to a long time attendee, Liz. Liz makes the drive up from Tucson every year to attend the event. Liz and I were chatting for a bit when she told me how much she loved Desert Code Camp and how much she has learned from it. She also told me a few tips and tricks that she learned over the years to be about to handle all of the learning that happens. She was kind enough to share this with me so I will share it with you.]]></summary></entry><entry><title type="html">Using SendGrid and emails are not being delivered to Hotmail/Outlook/Live/MSN.com recipients?</title><link href="https://www.josephguadagno.net/2017/05/17/using-sendgrid-and-email-are-not-being-delivered-to-hotmail-outlook-live-msn-com-recipients/" rel="alternate" type="text/html" title="Using SendGrid and emails are not being delivered to Hotmail/Outlook/Live/MSN.com recipients?" /><published>2017-05-17T10:03:56-07:00</published><updated>2017-05-17T10:03:56-07:00</updated><id>https://www.josephguadagno.net/2017/05/17/using-sendgrid-and-email-are-not-being-delivered-to-hotmail-outlook-live-msn-com-recipients</id><content type="html" xml:base="https://www.josephguadagno.net/2017/05/17/using-sendgrid-and-email-are-not-being-delivered-to-hotmail-outlook-live-msn-com-recipients/"><![CDATA[<blockquote>
  <p><em>Me too, or at least I was</em>… While the problem I was having might not be related to SendGrid exclusively, I am going to talk about the solution.</p>
</blockquote>

<h2 id="the-problem">The Problem</h2>

<p>Before I go into the solution, let’s talk about the problem first. Many of you know that I run the Desert Code Camp in Chandler, AZ. Originally, the site ran on the Microsoft stack; it used ASP.NET (Web Forms) and SQL Server. There is an API for mobile apps and future development, but that’s another story. The site has 12,000 registered users. A few thousand are SPAM/Bots, a few thousand are “inactive” accounts (opted out or moved), and the other 6000 or so are active users that have an interest in Desert Code Camp or have attended at least one.</p>

<p>And here lies the first problem.</p>

<p>When an announcement email goes out to our attendees or users of the site, we need to send out about 6000 emails. I used to do this by sending the email from a web page through code using <code class="language-plaintext highlighter-rouge">System.Net.SMTP</code> via <code class="language-plaintext highlighter-rouge">localhost.</code> At first, it wasn’t an issue. After a while, as Desert Code Camp grew, this became more and more of a problem. Typically the page would time out, or IIS would die or something. So I needed to find a solution. Sometime last year, probably around this time, I offloaded the processing of emails to <a href="https://azure.microsoft.com/en-us/services/storage/queues/?wt.mc_id=DT-MVP-4024623" target="_blank">Azure Queue Storage</a> and used <a href="https://docs.microsoft.com/en-us/azure/app-service-web/websites-webjobs-resources?wt.mc_id=DT-MVP-4024623" target="_blank">Azure WebJobs</a> to handle the “logic.” But wait, Azure does not support sending stuff via <code class="language-plaintext highlighter-rouge">localhost.</code> And that is true! This is where <a href="https://www.sendgrid.com/" target="_blank">SendGrid</a> comes into play. Azure provides, as of the creation of this post, a free 25,000 email per month subscription to SendGrid. For more on that, check out this <a href="https://docs.microsoft.com/en-us/azure/app-service-web/sendgrid-dotnet-how-to-send-email?wt.mc_id=DT-MVP-4024623" target="_blank">post</a>.</p>

<p>Now that I implemented the new Azure/SendGrid combination everything was great.</p>

<p>Fast forward a year, to this past weekend; I announced that the next <a href="https://oct2017.desertcodecamp.com" target="_blank">Desert Code Camp</a> was happening on Twitter and some people started submitting sessions. This is great! But I noticed that I was wasn’t getting my emails about the submissions. At first, I thought, “Oh Joe, you forgot something when you ‘created’ the new event.” It happens every time :smile:. However, this was not the case this time. Microsoft decided to <a href="https://sendgrid.com/docs/Classroom/Deliver/Sender_Authentication/microsoft_dmarc_changes.html" target="_blank">embrace</a> something called <a href="https://sendgrid.com/blog/dmarc-domain-based-message-authentication-reporting-conformance/" target="_blank">DMARC</a>, Domain-based Message Authentication, Reporting &amp; Conformance. In a nutshell, it checks to see if you are sending emails from the domain you claim you are sending them from, which for me was bad. Not because I was trying to be deceitful, but because I was sending emails saying they were from ‘<em>@hotmail.com</em>’ and sending them via ‘<em>SendGrid</em>’ because I wanted people to reply to my Hotmail address. Well, when Microsoft implemented the DMARC…</p>

<p><strong>What this means:</strong> As of June 2016, you can no longer send with the From address being anything from a Microsoft address when sending to a domain that checks DMARC before accepting mail.</p>

<p>… it saw my email as not being ‘<em>legitimate</em>’ and did not deliver it to my inbox and probably others.</p>

<h2 id="the-solution">The Solution</h2>

<p>The answer is ‘<a href="https://sendgrid.com/blog/dmarc-domain-based-message-authentication-reporting-conformance/" target="_blank">Whitelist</a>’ your domains while using SendGrid. There are more detailed instructions on how to whitelist a domain using SendGrid <a href="https://sendgrid.com/docs/Classroom/Basics/Whitelabel/index.html" target="_blank">here</a>.</p>

<h3 id="what-is-whitelabeling">What is Whitelabeling?</h3>

<p>Whitelabeling allows you to send through your own custom domain instead of SendGrid’s default settings. This will mask the header information of your emails with your data–not theirs–and will improve your email deliverability.</p>

<p><img src="/assets/images/posts/sendgrid-whitelabeling.png" alt="Send Grid - Non-Whitelabeled vs. Whitelabeled" class="align-center" /></p>

<p>Example Luckily, SendGrid makes it easy to whitelist your domain(s). Here is how you do it. <strong>Please note:</strong> <em>You will need access to your domain records, you will be making changes to your TXT or CNAME entries to prove you have rights to the domain</em>.</p>

<ul>
  <li>First, log-in to your SendGrid Account</li>
  <li>Click <code class="language-plaintext highlighter-rouge">Settings</code> (on the left)</li>
  <li>Click <code class="language-plaintext highlighter-rouge">Whitelabels</code></li>
  <li>Click <a href="#" class="btn btn--primary btn--small">+ Add Domain</a> button.</li>
  <li>SendGrid will walk you though everything else you need.</li>
</ul>

<h3 id="but-what-about-the-replies-to-hotmailcom">But what about the replies to @Hotmail.com</h3>

<p>I’m glad you are still with me :smile:. This part is the easy part. Essentially, I added ‘<em>noreply@desertcodecamp.com</em>’ as the ToAddress and added ‘<em>jguadagno@hotmail.com</em>’ as the ReplyTo. As shown here.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre><span class="kt">string</span> <span class="n">apiKey</span> <span class="p">=</span> <span class="n">ConfigurationManager</span><span class="p">.</span><span class="n">AppSettings</span><span class="p">[</span><span class="s">"SendGrid.ApiKey"</span><span class="p">];</span>
<span class="kt">var</span> <span class="n">sendGrid</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SendGridClient</span><span class="p">(</span><span class="n">apiKey</span><span class="p">);</span>

<span class="kt">var</span> <span class="n">msg</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">SendGridMessage</span><span class="p">();</span>
<span class="n">msg</span><span class="p">.</span><span class="nf">SetFrom</span><span class="p">(</span><span class="k">new</span> <span class="nf">EmailAddress</span><span class="p">(</span><span class="s">"noreply@yourdomain.com"</span><span class="p">,</span> <span class="s">"No Reply"</span><span class="p">));</span>
<span class="n">msg</span><span class="p">.</span><span class="nf">AddTo</span><span class="p">(</span><span class="k">new</span> <span class="nf">EmailAddress</span><span class="p">(</span><span class="s">"receiver@theirdomain.com"</span><span class="p">,</span> <span class="s">"Joe Dirt"</span><span class="p">));</span>
<span class="n">msg</span><span class="p">.</span><span class="nf">SetSubject</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">);</span>
<span class="n">msg</span><span class="p">.</span><span class="nf">AddContent</span><span class="p">(</span><span class="n">MimeType</span><span class="p">.</span><span class="n">Text</span><span class="p">,</span> <span class="s">"Hello World"</span><span class="p">);</span>
<span class="n">msg</span><span class="p">.</span><span class="nf">AddContent</span><span class="p">(</span><span class="n">MimeType</span><span class="p">.</span><span class="n">Html</span><span class="p">,</span> <span class="p">&lt;</span><span class="n">html</span><span class="p">&gt;&lt;</span><span class="n">body</span><span class="p">&gt;</span><span class="n">My</span> <span class="n">Body</span><span class="p">&lt;/</span><span class="n">body</span><span class="p">&gt;&lt;/</span><span class="n">html</span><span class="p">&gt;);</span>

<span class="k">if</span> <span class="p">(!</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">emailMessage</span><span class="p">.</span><span class="n">ReplyToMailAddress</span><span class="p">))</span>
<span class="p">{</span>
  <span class="n">msg</span><span class="p">.</span><span class="nf">SetReplyTo</span><span class="p">(</span><span class="k">new</span> <span class="nf">EmailAddress</span><span class="p">(</span><span class="s">"jguadagno@hotmail.com"</span><span class="p">,</span> <span class="s">"Joseph Guadagno"</span><span class="p">));</span>
<span class="p">}</span>

<span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="n">sendGrid</span><span class="p">.</span><span class="nf">SendEmailAsync</span><span class="p">(</span><span class="n">msg</span><span class="p">).</span><span class="n">Result</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I hope this saves you some time to troubleshoot mail delivery to @hotmail.com, @outlook.com, @live.com, @msn.com, @yahoo.com, and @gmail.com.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="gmail.com" /><category term="Hotmail.com" /><category term="Live.com" /><category term="msn.com" /><category term="Outlook.com" /><category term="SendGrid" /><category term="yahoo.com" /><category term="Web" /><summary type="html"><![CDATA[Me too, or at least I was… While the problem I was having might not be related to SendGrid exclusively, I am going to talk about the solution.]]></summary></entry><entry><title type="html">The Power of Community</title><link href="https://www.josephguadagno.net/2016/12/28/the-power-of-community/" rel="alternate" type="text/html" title="The Power of Community" /><published>2016-12-28T12:58:23-07:00</published><updated>2016-12-28T12:58:23-07:00</updated><id>https://www.josephguadagno.net/2016/12/28/the-power-of-community</id><content type="html" xml:base="https://www.josephguadagno.net/2016/12/28/the-power-of-community/"><![CDATA[<p>This blog post has been a long time coming. As a matter of fact, it has been on my Todo list for about two years. I know I am behind :smile:. While parts of this post might come off as bragging or boasting that is not the intent. The point I want to get across is how the community has helped me get to where I am today.</p>

<h2 id="the-back-story">The Back Story</h2>

<p>The journey started about 11 or so years ago when I first moved to Arizona. I had a few friends here, through my wife, and a job but I felt like I could not grow or expand in Arizona. I’ve spent most of my career building a name for myself in New York. After looking around at the technology scene in Arizona, there were a few groups around. AZGroups was the largest Microsoft focused group at the time. I decided to attend the meetings to network and to keep up with the latest and greatest in .NET. After a few months, I decided to, mostly because I didn’t feel like driving to AZGroups, to start my own user group in the Southeast Valley. Hence the “<a href="https://www.sevdnug.org" target="_blank">Southeast Valley .NET User Group</a>” was started. This launched my public speaking “career”.  After that, I started to see how networking and the community could help me and others grow.  A little bit later I started Tech Lunch South, a weekly lunch for technology people in the Southeast part of the Phoenix metro area.  It was great because I got to meet and talk with people in technology outside the Microsoft world. A bit later, I helped out with <a href="https://desertcodecamp.com" target="_blank">Desert Code Camp</a> and eventually became the sole organizer of it.</p>

<h2 id="the-effect">The Effect</h2>

<p>At this point you are probably saying “Blah Blah Blah Joe, I can see why you think this could be interpreted as bragging” :smile:.  Well, let me tell you the effect these community efforts had. The first was more speaking opportunities, I was able to speak at other groups in Arizona and California, in addition to Code Camps. Eventually, I got to speak at paid conferences, although I think I have Hurricane Sandy to thank for that :disappointed:. But the creating of the group or lunch or organizing Desert Code Camp was not the only reason for that, it was the networking.  Through the group, lunch, and code camp I have made a lot of friends and connections.  I also got a <a href="https://mvp.microsoft.com/en-us/PublicProfile/4024623?fullName=Joseph%20Guadagno" target="_blank">Microsoft MVP</a> out of it which help improved on those connections. I also got to help people along the way.</p>

<h2 id="connections">Connections</h2>

<p>As I eluded to earlier, these community groups that I started or continued, led to some really good connections. Some of these connections helped others:</p>

<ul>
  <li>One of the connections I made from this, Mike Hamilton (<a href="https://twitter.com/mikescott8">@mikescott8</a>){:target=”_blank”}, with a little bit of help from me started the <a href="https://nwvdnug.org/" target="_blank">Northwest Valley .NET User Group</a>.</li>
  <li>Another one, Barry Stahl (<a href="https://twitter.com/bsstahl" target="_blank">@bsstahl</a>) was able to start <a href="https://azgivecamp.org" target="_blank">AZGiveCamp</a></li>
  <li>Worked with the International .NET Association as Directory of Marketing, Vice President, and President to help start other users and to help them and speaker grow.</li>
</ul>

<p>Some of these connections helped me:</p>

<ul>
  <li>These connections had led to at least 2 different jobs and my current employer, <a href="https://www.rocketmortgage.com" target="_blank">Rocket Mortgage</a>.</li>
  <li>Offers to speak at several US based conferences, like <a href="https://www.devintersection.com" target="_blank">DEVIntersections</a>, <a href="https://www.itdevconnections.com/dc16/Public/Enter.aspx" target="_blank">DEVConnections</a>, <a href="https://www.codestock.org" target="_blank">Code Stock</a>, <a href="https://www.techbash.com/" target="_blank">TechBash</a>, <a href="https://www.mvpmix.com/dallas" target="_blank">MVP Mix Dallas</a>, and others</li>
  <li>Offers to speak at two international ones, <a href="https://conference.c-sharpcorner.com/" target="_blank">C# Corner Conference</a>and <a href="https://ndc-london.com/" target="_blank">NDC London</a>.</li>
  <li>I was even on .<a href="https://dotnetrocks.com/?show=1255" target="_blank">NET Rocks</a> talking about <a href="https://azure.microsoft.com/en-us/services/application-insights/?wt.mc_id=DT-MVP-4024623" target="_blank">Application Insights</a>.</li>
</ul>

<h2 id="the-bottom-line">The Bottom Line</h2>

<p>The moral of the story is to try and not be the “A-typical” developer and socialize at events. Use the user group, code camp, or conference to build up a network of peers to make you and them better.  If you are looking for help on how to socialize at these events, one of my friends and fellow speaker Jeremy Clark (<a href="https://twitter.com/jeremybytes" target="_blank">@jeremybytes</a>) <a href="https://jeremybytes.blogspot.com/2014/12/becoming-social-developer-guide-for.html" target="_blank">blogged</a> about it. Have a “Power of the Community” story to share, post a comment below.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Community" /><category term="Microsoft MVP" /><category term="MVP" /><summary type="html"><![CDATA[This blog post has been a long time coming. As a matter of fact, it has been on my Todo list for about two years. I know I am behind :smile:. While parts of this post might come off as bragging or boasting that is not the intent. The point I want to get across is how the community has helped me get to where I am today.]]></summary></entry><entry><title type="html">Book Review: Practical Microsoft Visual Studio 2015</title><link href="https://www.josephguadagno.net/2016/12/20/book-review-practical-microsoft-visual-studio-2015/" rel="alternate" type="text/html" title="Book Review: Practical Microsoft Visual Studio 2015" /><published>2016-12-20T13:03:57-07:00</published><updated>2016-12-20T13:03:57-07:00</updated><id>https://www.josephguadagno.net/2016/12/20/book-review-practical-microsoft-visual-studio-2015</id><content type="html" xml:base="https://www.josephguadagno.net/2016/12/20/book-review-practical-microsoft-visual-studio-2015/"><![CDATA[<p>While the title of the book is “Practical Microsoft Visual Studio 2015”, the author, Peter Ritchie, outlines how you can use Microsoft Visual Studio to effectively develop software and lead a team. Peter covers, a quick walkthrough of Microsoft Visual Studio, how to work with teams, version control, design and architecture, development, deployments, and testing. Anyone getting started with Microsoft Visual Studio or even experienced engineers, architects and leaders can find stuff in this that is useful for their day to day tasks. The best part of this book is the way the author explains each topic in a simple way based on his 20 or so ( :smile: ) years being in the software field.</p>

<iframe style="width: 120px; height: 240px;" src="//ws-na.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=US&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=josephguadagno-20&amp;marketplace=amazon&amp;region=US&amp;placement=1484223128&amp;asins=1484223128&amp;linkId=15ca6cae334f7e2143adb8fa8c789771&amp;show_border=true&amp;link_opens_in_new_window=true" width="300" height="150" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>

<p class="notice"><strong>Disclaimer:</strong> I was one of the technical reviewers of this book.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><category term="Visual Studio" /><summary type="html"><![CDATA[While the title of the book is “Practical Microsoft Visual Studio 2015”, the author, Peter Ritchie, outlines how you can use Microsoft Visual Studio to effectively develop software and lead a team. Peter covers, a quick walkthrough of Microsoft Visual Studio, how to work with teams, version control, design and architecture, development, deployments, and testing. Anyone getting started with Microsoft Visual Studio or even experienced engineers, architects and leaders can find stuff in this that is useful for their day to day tasks. The best part of this book is the way the author explains each topic in a simple way based on his 20 or so ( :smile: ) years being in the software field.]]></summary></entry><entry><title type="html">A Tester and/or Unit Tests Should Be Your Friend</title><link href="https://www.josephguadagno.net/2016/09/22/a-tester-andor-unit-tests-should-be-your-friend/" rel="alternate" type="text/html" title="A Tester and/or Unit Tests Should Be Your Friend" /><published>2016-09-21T23:00:18-07:00</published><updated>2016-09-21T23:00:18-07:00</updated><id>https://www.josephguadagno.net/2016/09/22/a-tester-andor-unit-tests-should-be-your-friend</id><content type="html" xml:base="https://www.josephguadagno.net/2016/09/22/a-tester-andor-unit-tests-should-be-your-friend/"><![CDATA[<p>A little bit over three years when I started at Rocket Mortgage as a Software Architect I did not know/understand how valuable the Quality Analyst (QA) or testing role was to a product or application. When I started, on Day 2, Brian Friesen introduced me to a lot of people but one of the introductions I remember the most was to <a href="https://www.twitter.com/g33klady" target="_blank">Hilary Weaver-Robb</a> (<a href="https://g33klady.wordpress.com/" target="_blank">https://g33klady.wordpress.com/</a>). The reason why I remember this is because she had since built a conference talk around it. Titled something like “QA and Developers should be best of friends” or something like that… :smile: The story goes like this…</p>

<p>Day 2 at a new place after spending a FULL day in front of the company fire hose getting introduced to everything that is Rocket Mortgage, Brian introduced me to my new surrogate team (I was on loan for few months). The introductions went along, and eventually, Brian introduces me to Hilary (some know her as Heather). Brian says “This is Hilary, our QA.” I immediately, mostly jokingly, say “Ugh.” We all chuckled, although I believe Hilary, excuse me, Heather, secretly started hating me. And while I was partly joking at the time, in my experience before meeting Hilary, I’ve never understood the value of a Quality Analyst. Until now. Whether Hilary wants to believe it or not, I consider her a good friend, although I am not sure the feeling is mutual.</p>

<blockquote class="twitter-tweet" data-lang="en"><p lang="und" dir="ltr"> <a href="https://t.co/0xWqV92Key">pic.twitter.com/0xWqV92Key</a></p>&mdash; Hilary aka H-Bomb (@g33klady) <a href="https://twitter.com/g33klady/status/778664643116011521?ref_src=twsrc%5Etfw">September 21, 2016</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>Anyway, the real reason for this post. In the last three years at Rocket Mortgage, I have learned the value of writing Unit test and having testers on a team. There have been far too many times (at least one :smile: ) where I have pushed code for my personal projects (Desert Code Camp, MVP Summit Events, etc.) and things have failed miserably. So, I have gone back to all of my personal projects and have added Unit Tests and you should too! Not just in your personal projects but your professional ones. I know, it takes more time to write a Unit test or I write it the first time. <em>Believe me, it doesn’t, and you don’t</em>. You’ll probably spend more time researching and fixing your problem that you would have “wasted” on building a unit test. So just do it!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="qa" /><category term="testing" /><category term="uat" /><summary type="html"><![CDATA[A little bit over three years when I started at Rocket Mortgage as a Software Architect I did not know/understand how valuable the Quality Analyst (QA) or testing role was to a product or application. When I started, on Day 2, Brian Friesen introduced me to a lot of people but one of the introductions I remember the most was to Hilary Weaver-Robb (https://g33klady.wordpress.com/). The reason why I remember this is because she had since built a conference talk around it. Titled something like “QA and Developers should be best of friends” or something like that… :smile: The story goes like this…]]></summary></entry><entry><title type="html">15 Years Ago, I Witnessed Something That I Will Never Forget</title><link href="https://www.josephguadagno.net/2016/09/11/15-years-ago-i-witnessed-something-that-i-will-never-forget/" rel="alternate" type="text/html" title="15 Years Ago, I Witnessed Something That I Will Never Forget" /><published>2016-09-10T22:46:31-07:00</published><updated>2016-09-10T22:46:31-07:00</updated><id>https://www.josephguadagno.net/2016/09/11/15-years-ago-i-witnessed-something-that-i-will-never-forget</id><content type="html" xml:base="https://www.josephguadagno.net/2016/09/11/15-years-ago-i-witnessed-something-that-i-will-never-forget/"><![CDATA[<p><img src="https://upload.wikimedia.org/wikipedia/en/thumb/4/43/Seconds_after_first_plane.JPG/250px-Seconds_after_first_plane.JPG" alt="image-right" title="North Tower Plane Crash" class="align-right" />
There is an expression in the US and part of the world that goes ‘9/11 Never Forget’. <strong>I will never forget 9/11!</strong> I remember virtually every detail of those days and the weeks that proceeded. From just after the first plane hit, until two or three weeks after the ‘event.’ Here is my tale. It started as any other ordinary day. I took a bus to commuter train (LIRR) to the NY City subway to get to work at Guardian Life Insurance. I always got in early, around 7:00 to beat the rush. I was in my cubicle probably checking email or coding when a few coworkers came and said, “Do you see the World Trade Center is on fire?” At the time, I worked in downtown Manhattan at <a href="https://www.bing.com/mapspreview?q=7+hanover+square+new+york+ny+10004+guardian&amp;mkt=en&amp;FORM=HDRSC4" target="_blank">7 Hanover Sq</a> which was about a half of a mile from The World Trade Center. I replied “No” and I followed the people to the other side of the building where I watched the North Tower, 1 World Trade Center, smoke from what we know now as the first plane to hit the building. We watched in awe as the building was smoking and papers were flying around like it was snow at Christmas.</p>

<p>We stood, the 6 of us, amazed the World Trade Center was on fire. Seventeen minutes later at 9:03 am EDT I and the six others were shocked when we saw the second plane hit the South Tower (2 World Trade Center).</p>

<p><img src="https://upload.wikimedia.org/wikipedia/en/e/ed/Wpix-911.jpg" alt="image-right" title="Tower 2 hit" class="align-right" /></p>

<p>I was shocked! I watched as the plane, as we know now, as the second plane to fly in the sky and HIT the second building. ALL of us were like, “what the f#@#!?”. We had no idea what happened. We all scattered. like the NYC cockroaches do when the lights turned on and went back to our desks. I, like all the others, went back to our desks/cubicles. I called my wife (<a href="https://twitter.com/deeguadagno" target="_blank">@deeguadagno</a>) in a panic… “Did you see? I just saw a plane hit the World Trade Center?” Both of us at the time did not know what happened; I said to her “I’m out of here,” I told my boss at the time. “I just saw a plane hit the World Trade Center and both of them are on fire. I’m leaving!”. He, as most corporate managers would do said, “We do not have permission to leave yet.”. I said, “I don’t care, I’m leaving.” I left and called my direct reports and told them to head home if there were already in transit. As I walked the 4-7 blocks from the office to the subway station, it felt like I was walking through dirty snow. There were paper and soot from the fires and ashes from the World Trade Center that was blocks away. I eventually got on a subway and the last commuter rail (LIRR) out of Penn station heading for home. At the time, I did not have a cell phone, so I had to borrow someone else to call the wife and tell her I made it out of the City and was heading home. The other passengers and I were watching the World Trade Center smoke from a distance. I finally arrived home around 10:40 am where I hugged my wife and two kids to learned that both Towers have collapsed. I did not know what to do or say other then hug my family. I eventually went did some of the ‘Honey Do List’ items to take my mind off of what happened. While doing that, I tried to find out how some of my direct reports were doing. One was in Denver on training; two were en route to work. Unfortunately, as a result of the ‘attacks,’ the telephone network was damaged. It took almost 24 hours until I found out that all of my direct reports were OK. The weird thing about the event was that we, as a Family, were supposed to go on a vacation to LA on September 12th to go to Disney, Sea World, Hollywood, and Southern California. Unfortunately, as we all know, flights were ground for a bit. While Dee was glued to the TV, I couldn’t watch it and did a lot of my ‘honey do list’ stuff like mow the lawn, fix stuff, anything to avoid the TV and the realization of what happened until the flights we no longer grounded. Around September 14th or so, flights were allowed to take off again. Personally, outside of all of the tragedy, this one of the smoothest vacations around. We, a family of 4, were on a 737 or so aircraft with a configuration of 2 x 4 x 2. This plane could probably sit about 300 people or so. There were about 30 of us on the plane which meant each person had a row to themselves for the 4-hour flight. While on the trip, since no one seemed to be flying or vacationing at the time. we essentially had <a href="https://disneyworld.disney.go.com" target="_blank">Disney World</a>, <a href="https://www.universalstudioshollywood.com/" target="_blank">Universal Studios</a>, <a href="https://www.legoland.com/" target="_blank">LegoLand</a> and <a href="https://seaworldparks.com/en/seaworld-sandiego/" target="_blank">SeaWorld</a> to ourselves. The trip was great! There were no lines at the parks, we went on rides multiple times and enjoyed life… Fourteen days later I returned to reality. I got up at 5:00am and started my commute into work. This was roughly when “Lower Manhattan” was open to businesses. Unfortunately, at the time, the trains only went to about 14th street because the City was inspecting the rails and tunnels as a result of the collapse. This meant that my commute went from about an hour and a half to about two hours and fifteen minutes with about two miles of walking. The time or walk did not matter. It was more of ‘the walk’ or how we had to walk that mattered. The furthest south that the subway would stop was around 14th and Broadway. The easiest walk to our office was straight down Broadway. Broadway, for those of you that do not know NYC, ran parallel, one block away from the World Trade Center. That means that even day, to and from work, I walked by what was left of the World Trade Center. Let me tell that it SUCKED for a minimum of two weeks. Once I, and other commuters, got to the vicinity of the World Trade Center at was at an intersection we all stopped, looked and cried. Because of the different angles and the length of the ‘site,from’ this went on for about a half of a mile. Let me tell you it sucked! It was rough going into and leaving work every day and having to for 30 minutes or so as part of your ‘commute’.Don’t get me wrong, my crying and longer commute time do not compare to the 3000+ people that lost their lives or loved ones that still have to suffer. So 15 years later I still remember the day as it was yesterday. It’s still hard to see a plane in the sky and not picture the plane I saw hit the South Tower. However, you have to remember that we, as a Country, have and will more on. Fast forward about 20 or so months later to the <a href="https://en.wikipedia.org/wiki/Northeast_blackout_of_2003" target="_blank">Northeast Blackout of 2003</a> when NYC and others lost power. We had NO idea what happened. We all assumed another 9/11 type attack. We all panicked and migrated/ran/left the city. I walked home from the office, about 18 miles, in 85-degree heat and about 1000% humidity, until I got to my brother’s apartment. On route, I experienced people charging $5 - $10 for a 16oz bottle of water. While I can imagine what others felt around the tragedies of 9/11 or what the survivors or sufferers have been through, <strong>I will Never Forget 9/11</strong>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Personal" /><summary type="html"><![CDATA[There is an expression in the US and part of the world that goes ‘9/11 Never Forget’. I will never forget 9/11! I remember virtually every detail of those days and the weeks that proceeded. From just after the first plane hit, until two or three weeks after the ‘event.’ Here is my tale. It started as any other ordinary day. I took a bus to commuter train (LIRR) to the NY City subway to get to work at Guardian Life Insurance. I always got in early, around 7:00 to beat the rush. I was in my cubicle probably checking email or coding when a few coworkers came and said, “Do you see the World Trade Center is on fire?” At the time, I worked in downtown Manhattan at 7 Hanover Sq which was about a half of a mile from The World Trade Center. I replied “No” and I followed the people to the other side of the building where I watched the North Tower, 1 World Trade Center, smoke from what we know now as the first plane to hit the building. We watched in awe as the building was smoking and papers were flying around like it was snow at Christmas.]]></summary></entry><entry><title type="html">Desert Code Camp</title><link href="https://www.josephguadagno.net/2015/10/01/desert-code-camp/" rel="alternate" type="text/html" title="Desert Code Camp" /><published>2015-10-01T03:21:25-07:00</published><updated>2015-10-01T03:21:25-07:00</updated><id>https://www.josephguadagno.net/2015/10/01/desert-code-camp</id><content type="html" xml:base="https://www.josephguadagno.net/2015/10/01/desert-code-camp/"><![CDATA[<p>I posted on Twitter today that Desert Code Camp is not happening this year.  The venue that we use, Chandler-Gilbert Community College is going through some renovations and just could not fit us in this year, unfortunately.  A few people asked what we would need from a venue to help make the Desert Code Camp happen.  I put together a list below:</p>

<ul>
  <li>Should be able to support 700-900 people (it grows about 100 people every Code Camp)</li>
  <li>Should have at least 15 rooms
    <ul>
      <li>Rooms should have a projector</li>
      <li>At least seven rooms should be able to accommodate 30 people</li>
      <li>At least three rooms should be able to accommodate 75 people</li>
      <li>Preferably have three rooms that can support 100+ people.</li>
    </ul>
  </li>
  <li>There should be a large area to host breakfast, lunch, and a sponsor area for all of the attendees</li>
  <li>The venue should have reliable WiFi for the attendees</li>
  <li>Preferably free or very low-cost since this event is free</li>
</ul>

<p>Some nice to have:</p>

<ul>
  <li>A room/venue large enough to hold all of the attendees for an opening and closing keynote.</li>
  <li>Amble (free) parking for the attendees.</li>
</ul>

<p>If you have any questions or would like to throw your venue in the hat, please reach out to me.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="News" /><category term="Arizona Technology News" /><category term="Desert Code Camp" /><summary type="html"><![CDATA[I posted on Twitter today that Desert Code Camp is not happening this year. The venue that we use, Chandler-Gilbert Community College is going through some renovations and just could not fit us in this year, unfortunately. A few people asked what we would need from a venue to help make the Desert Code Camp happen. I put together a list below:]]></summary></entry><entry><title type="html">So you want to be a Microsoft MVP</title><link href="https://www.josephguadagno.net/2015/05/14/so-you-want-to-be-a-microsoft-mvp/" rel="alternate" type="text/html" title="So you want to be a Microsoft MVP" /><published>2015-05-14T11:03:21-07:00</published><updated>2023-10-19T16:30:00-07:00</updated><id>https://www.josephguadagno.net/2015/05/14/so-you-want-to-be-a-microsoft-mvp</id><content type="html" xml:base="https://www.josephguadagno.net/2015/05/14/so-you-want-to-be-a-microsoft-mvp/"><![CDATA[<p>It’s very common for myself and other Microsoft MVPs to get asked “What does it take to become a <a href="http://mvp.microsoft.com/en-us/overview.aspx" target="_blank">Microsoft MVP</a>” or “I want to be a Microsoft MVP, please tell me what I can do” when we speak at conferences or chat with each other on social networks. The short answer is most people outside of Microsoft do not know that the “secret formula” is. The long answer requires a little bit of understanding of how Microsoft evaluates potential candidates for receiving the award and what the propose of the award is. The rest of this post is my opinion on how you can become a Microsoft MVP. To see what Microsoft says about how you can become a Microsoft MVP visit <a href="http://mvp.microsoft.com/en-us/becoming-an-mvp.aspx" target="_blank">http://mvp.microsoft.com/en-us/becoming-an-mvp.aspx</a> For one to earn the Microsoft MVP award, Microsoft has to see the recipient as someone who provides value to the Microsoft ecosystem and is influential in the software industry. The award is given based on the previous year’s contributions to the Microsoft ecosystem and is from one of the product teams. Product teams include Microsoft Office, Microsoft Azure, Microsoft .NET and many others. Each product group budgets for a certain number of MVPs a year, depending on the specialty, there can be very few spots to many spots available to for new candidates. In addition to this, you need to get <a href="http://mvp.microsoft.com/en-us/nominate-an-mvp.aspx" target="_blank">nominated</a> by a Microsoft MVP or Microsoft employee for Microsoft to consider you. OK, so how can I provide value to the Microsoft ecosystem you ask. That’s the tricky spot; there are no fixed rules, minimum contributions or formula for getting in. That being said there are a few things you can do to get on the radar:</p>

<ul>
  <li>you can blog</li>
  <li>speak at conference/user groups</li>
  <li>respond to forum post on <a href="https://social.msdn.microsoft.com/Forums/en-US/home" target="_blank">MSDN</a>, <a href="http://stackoverflow.com/" target="_blank">Stack Overflow</a>, <a href="http://www.c-sharpcorner.com/Forums/" target="_blank">C# Corner</a> and similar forums</li>
  <li>help out your local user groups and Microsoft Developer Evangelists at events</li>
  <li>contribute to open source projects, especially ones that Microsoft is interested in :) (Thanks <a href="https://twitter.com/jamie_dixon" target="_blank">@jamie_dixon</a> for the tip)</li>
</ul>

<p>My recommendation is that you do not make becoming a Microsoft MVP a goal, but let it happen naturally by doing some of the things mentioned above and even if you do not get the Microsoft MVP award, remember that you are still helping people with your contributions.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Microsoft MVP" /><category term="MVP" /><summary type="html"><![CDATA[It’s very common for myself and other Microsoft MVPs to get asked “What does it take to become a Microsoft MVP” or “I want to be a Microsoft MVP, please tell me what I can do” when we speak at conferences or chat with each other on social networks. The short answer is most people outside of Microsoft do not know that the “secret formula” is. The long answer requires a little bit of understanding of how Microsoft evaluates potential candidates for receiving the award and what the propose of the award is. The rest of this post is my opinion on how you can become a Microsoft MVP. To see what Microsoft says about how you can become a Microsoft MVP visit http://mvp.microsoft.com/en-us/becoming-an-mvp.aspx For one to earn the Microsoft MVP award, Microsoft has to see the recipient as someone who provides value to the Microsoft ecosystem and is influential in the software industry. The award is given based on the previous year’s contributions to the Microsoft ecosystem and is from one of the product teams. Product teams include Microsoft Office, Microsoft Azure, Microsoft .NET and many others. Each product group budgets for a certain number of MVPs a year, depending on the specialty, there can be very few spots to many spots available to for new candidates. In addition to this, you need to get nominated by a Microsoft MVP or Microsoft employee for Microsoft to consider you. OK, so how can I provide value to the Microsoft ecosystem you ask. That’s the tricky spot; there are no fixed rules, minimum contributions or formula for getting in. That being said there are a few things you can do to get on the radar:]]></summary></entry><entry><title type="html">Being More Productive with @Todoist</title><link href="https://www.josephguadagno.net/2015/03/13/being-more-productive-with-todoist/" rel="alternate" type="text/html" title="Being More Productive with @Todoist" /><published>2015-03-13T03:55:41-07:00</published><updated>2015-03-13T03:55:41-07:00</updated><id>https://www.josephguadagno.net/2015/03/13/being-more-productive-with-todoist</id><content type="html" xml:base="https://www.josephguadagno.net/2015/03/13/being-more-productive-with-todoist/"><![CDATA[<p>It’s not unusual nowadays for the typical person to be overwhelmed with tasks that we need to get accomplished on a day to day, week to week or just plain need to get done. Whether it’s for work, home, or just some personal tasks, we look to different ways of tracking these items or ways to remind us that we have something to do.  Some go old school and tie a ribbon or thread to a finger :smile: Some write down tasks in Franklin planners or equivalent tools. Some followup methods that have been created by “productivity experts”. For me, I used a combination of my multiple emails accounts; work, personal x 2, and communities emails address along with PostIt notes. After my work email box got up to about 100 items I needed to act on and my personal got to about 75, I said enough is enough, I needed to find a better way to track the work I was working and when it was do, so I could better prioritize my day.  After searching the web and trying out a few applications I landed on <a href="https://www.todoist.com/" target="_blank">Todoist</a>. Todoist basically provides a service to store your “TODOs”. However, Todoist is not just a todo list. The value is in the meta data that you add to the todo. Things like projects, labels, due dates. reminders, and notes.  Granted some of these features are extra, but at a cost of <a href="https://todoist.com/premium" target="_blank">$36 a year</a>, it is reasonable. Todoist is also available on multiple browsers, platforms, phones and plugins to web mail.  Look <a href="https://todoist.com/Help/Apps" target="_blank">here</a> for the full list.</p>

<h2 id="the-application">The Application</h2>

<h2 id="main">Main</h2>

<p><img src="/assets/images/posts/todoist-main.png" alt="Todoist Interface" class="align-center" /></p>

<p>As you can see the interface is quite simple. On the left, we have three links to “views” of your todo list. ‘Inbox” is used for anything that you did not assign to a project. “Today’ contains any items that are due today. ‘Next 7 days’ contain a list of the items due in the next seven days. There are then links to the projects, that you create with the color your assigned them.  The slightly grayed out number represents how many items are in the view or project.</p>

<h3 id="right-side">Right Side</h3>

<p>To the right, we have the todo list view.  It contains a header</p>

<p><img src="/assets/images/posts/todoist-right-side.png" alt="Todoist Header Bar" class="align-center" /></p>

<p>The header contains the current filter applied (mine is overdue and next seven days). A plus button to add a new task. A bell for you to see the application notifications. The shield and numbers represent your todo <a href="https://todoist.com/Help/Karma" target="_blank">Karma</a> rating and how it is trending.</p>

<h3 id="adding-a-task">Adding a Task</h3>

<p><img src="/assets/images/posts/todoist-add-task.png" alt="Todoist - Adding a Task" class="align-center" /></p>

<p>Adding a task is easy. Though out the desktop application the are multiple locations to add tasks and they all look like the image above.  You simply type the text you want and optionally set a due date. Due dates can be set by selecting from the popup calendar or typing in text like ‘Every Friday’.  See <a href="https://todoist.com/Help/DatesTimes" target="_blank">here</a> for date options. Overall the product is simple to use, and it has helped me a lot in the last month.</p>

<p class="notice"><strong>Just a note</strong>, I did not receive anything for this blog post. I wrote the post because the application/service has been beneficial for me and I wanted to share.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Productivity" /><summary type="html"><![CDATA[It’s not unusual nowadays for the typical person to be overwhelmed with tasks that we need to get accomplished on a day to day, week to week or just plain need to get done. Whether it’s for work, home, or just some personal tasks, we look to different ways of tracking these items or ways to remind us that we have something to do. Some go old school and tie a ribbon or thread to a finger :smile: Some write down tasks in Franklin planners or equivalent tools. Some followup methods that have been created by “productivity experts”. For me, I used a combination of my multiple emails accounts; work, personal x 2, and communities emails address along with PostIt notes. After my work email box got up to about 100 items I needed to act on and my personal got to about 75, I said enough is enough, I needed to find a better way to track the work I was working and when it was do, so I could better prioritize my day. After searching the web and trying out a few applications I landed on Todoist. Todoist basically provides a service to store your “TODOs”. However, Todoist is not just a todo list. The value is in the meta data that you add to the todo. Things like projects, labels, due dates. reminders, and notes. Granted some of these features are extra, but at a cost of $36 a year, it is reasonable. Todoist is also available on multiple browsers, platforms, phones and plugins to web mail. Look here for the full list.]]></summary></entry><entry><title type="html">Speakers are not Idols</title><link href="https://www.josephguadagno.net/2015/03/03/speakers-are-not-idols/" rel="alternate" type="text/html" title="Speakers are not Idols" /><published>2015-03-03T07:00:00-07:00</published><updated>2015-03-03T07:00:00-07:00</updated><id>https://www.josephguadagno.net/2015/03/03/speakers-are-not-idols</id><content type="html" xml:base="https://www.josephguadagno.net/2015/03/03/speakers-are-not-idols/"><![CDATA[<p>I’ve noticed that at a lot of conferences I attend conference attendees are afraid to approach the conference speakers. It would appear that some attendees think that speakers are idols and untouchable. I want to say that conference speakers are not <em>idols</em>. While I can not speak for every conference speaker, I know myself, and the other conference speakers that I know, love when conference attendees come up to us. So when you are at your next conference, or user group meeting, feel free to walk up to the speaker after the talk or if you see them in the hallway.</p>

<p>Here are a few tips for interacting with a speaker:</p>

<ul>
  <li>Be respectful of the speakers time. Do not monopolize their time.</li>
  <li>Do not expect the speaker to solve all your problems. Most will spend a few minutes to talk to you and to understand the issue so that they can provide suggestions; some may provide guidance.</li>
  <li>Feel free to contact them.  Most speakers provide an email address and/or have social media accounts.  Please keep in mind that some speakers keep their Facebook accounts personal so do not be offended if they deny the request or do not approve it.</li>
  <li>Avoid interrupting speakers if they are already talking to someone.</li>
  <li>Avoid interrupting speakers while they are eating, if they are at your table they will most likely talk.</li>
</ul>

<p>I’d love your feedback; please leave comments below with your thoughts.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Community" /><category term="Speaking" /><category term="Events" /><summary type="html"><![CDATA[I’ve noticed that at a lot of conferences I attend conference attendees are afraid to approach the conference speakers. It would appear that some attendees think that speakers are idols and untouchable. I want to say that conference speakers are not idols. While I can not speak for every conference speaker, I know myself, and the other conference speakers that I know, love when conference attendees come up to us. So when you are at your next conference, or user group meeting, feel free to walk up to the speaker after the talk or if you see them in the hallway.]]></summary></entry><entry><title type="html">Bootstrap for ASP.NET MVC Review</title><link href="https://www.josephguadagno.net/2015/02/28/bootstrap-for-asp-net-mvc-review/" rel="alternate" type="text/html" title="Bootstrap for ASP.NET MVC Review" /><published>2015-02-28T11:40:00-07:00</published><updated>2015-02-28T11:40:00-07:00</updated><id>https://www.josephguadagno.net/2015/02/28/bootstrap-for-asp-net-mvc-review</id><content type="html" xml:base="https://www.josephguadagno.net/2015/02/28/bootstrap-for-asp-net-mvc-review/"><![CDATA[<p>Starts off with a good introduction to using Bootstrap in your ASP.NET applications. It covers the default “File | New” experience, manually added files and my favorite, NuGet. The author then continues with introducing the Bootstrap element styles in a logical order, starting with the grid and ending with form validation.  Next the author introduces the JavaScript components of Bootstrap.</p>

<p>In the next part of the book the author does what I have not seen in other books on Bootstrap, the author explains how to create helper methods, which is good but there already is a well used package for that in which the author has a chapter on on at the end of the book.</p>

<p>The author goes on to give suggestions are to how to work with and convert sites that do not have bootstrap.</p>

<p>I’m not sure why but the author included a whole chapter using the jQuery DataTables plugin with some of it’s extensions with Bootstrap.</p>

<p>Overall, the book was easy to read, the author was clear and logical in his explanations of Bootstrap. I would recommend this book to someone just starting to learn Bootstrap and working on ASP.NET applications.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><category term="Bootstrap" /><category term="Twitter Bootstrap" /><summary type="html"><![CDATA[Starts off with a good introduction to using Bootstrap in your ASP.NET applications. It covers the default “File | New” experience, manually added files and my favorite, NuGet. The author then continues with introducing the Bootstrap element styles in a logical order, starting with the grid and ending with form validation.  Next the author introduces the JavaScript components of Bootstrap.]]></summary></entry><entry><title type="html">Pro NuGet Book Review</title><link href="https://www.josephguadagno.net/2015/02/28/pro-nuget-book-review/" rel="alternate" type="text/html" title="Pro NuGet Book Review" /><published>2015-02-28T11:38:00-07:00</published><updated>2015-02-28T11:38:00-07:00</updated><id>https://www.josephguadagno.net/2015/02/28/pro-nuget-book-review</id><content type="html" xml:base="https://www.josephguadagno.net/2015/02/28/pro-nuget-book-review/"><![CDATA[<p>First off, I love the subtitle “Your Salvation from Dependency Hell’, although NuGet does offer its own Dependency Hell sometimes.</p>

<p>The first chapter starts out as you would expect, how to get started with NuGet.  However, the authors  added a helpful page of links to the different NuGet tools and documentation that they would be referring to.</p>

<p>The next chapter goes through, in great detail, how to install, uninstall and upgrade NuGet from the user interface, NuGet Powershell Console and command line. They then continue to talk about NuGets solution package management and visualization features.</p>

<p>The third chapter is a doozy, the authors explain in great detail how to author a NuGet package. They cover versioning, dependencies, configuration transformations and more.</p>

<p>The next chapter covers publishing NuGet package in three different ways; on NuGet.org, through the command line, and NuGet Package Explorer. The authors continue the chapter by explaining how to publish the symbol files for you NuGet packages.</p>

<p>The authors then show you how you can host your own NuGet repository on your servers or use a service like <a href="https://www.myget.org" target="_blank">MyGet.org</a> to host your packages privately.</p>

<p>The next three chapters talk about integrating NuGet into you release management process for both continuous and automated delivery as well as integration with your build services (TFS and Team City)</p>

<p>The final two chapters talk about extending NuGet to add features to it and using NuGet to make your applications extendable.</p>

<p>Overall, this books first 5 chapters starts with what you would expect a book or a set of blog post on NuGet would. However, the authors provided a lot more than just the “normal” stuff, they added a lot of extra detail and reference points.  But what really sets this book apart from the rest is the last 5 chapters that go into how you can incorporate NuGet into your current environment and processes. It also provides lots of ideas on how you can custom NuGet and use NuGet to make your own application extensible.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><category term="NuGet" /><summary type="html"><![CDATA[First off, I love the subtitle “Your Salvation from Dependency Hell’, although NuGet does offer its own Dependency Hell sometimes.]]></summary></entry><entry><title type="html">jQuery Twitter Bootstrap Flickr Carousel</title><link href="https://www.josephguadagno.net/2014/11/18/jquery-twitter-bootstrap-flickr-carousel/" rel="alternate" type="text/html" title="jQuery Twitter Bootstrap Flickr Carousel" /><published>2014-11-18T09:06:00-07:00</published><updated>2014-11-18T09:06:00-07:00</updated><id>https://www.josephguadagno.net/2014/11/18/jquery-twitter-bootstrap-flickr-carousel</id><content type="html" xml:base="https://www.josephguadagno.net/2014/11/18/jquery-twitter-bootstrap-flickr-carousel/"><![CDATA[<p>A few months ago I built a jQuery plugin that uses the Twitter <a href="https://www.getbootstrap.com" target="_blank">Bootstrap</a> <a href="https://getbootstrap.com/javascript/#carousel" target="_blank">Carousel</a> to cycle through images on <a href="https://www.flickr.com" target="_blank">Flickr</a>. In order to use this plugin you need to get an <a href="https://www.flickr.com/services/api/misc.api_keys.html" target="_blank">Api</a> Key from Flickr and have the following software:</p>

<ul>
  <li>jQuery (v1.8 or higher)</li>
  <li>Twitter Bootstrap (v3.0 or higher)</li>
  <li>Twitter Bootstrap components (v3.0 or higher)</li>
  <li><a href="https://github.com/esimakin/twbs-pagination" target="_blank">twbsPagination</a> (optional)</li>
</ul>

<h2 id="sample-usage">Sample Usage</h2>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#flickr-carousel</span><span class="dl">'</span><span class="p">).</span><span class="nx">twbsFlickrCarousel</span><span class="p">(</span>
  <span class="p">{</span>
    <span class="na">tagsToSearchFor</span><span class="p">:</span> <span class="dl">'</span><span class="s1">mvpsummit,mvp2013,mvp13</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">flickrApiKey</span><span class="p">:</span> <span class="dl">'</span><span class="s1">insert your key here</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">paginationSelector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#flickr-pagination</span><span class="dl">'</span>
  <span class="p">}</span>
<span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Download the plugin from GitHub at: <a href="https://github.com/jguadagno/twbs-flickrCarousel" title="https://github.com/jguadagno/twbs-flickrCarousel" target="_blank">https://github.com/jguadagno/twbs-flickrCarousel</a></p>

<p>See an example of usage at: <a href="https://mvp2014.mvpsummitevents.info/flickr" target="_blank">https://mvp2014.mvpsummitevents.info/flickr</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Bootstrap" /><category term="Carousel" /><category term="Flickr" /><category term="jQuery" /><category term="jQuery Plugin" /><category term="Plugin" /><category term="Twitter Bootstrap" /><category term="Web" /><summary type="html"><![CDATA[A few months ago I built a jQuery plugin that uses the Twitter Bootstrap Carousel to cycle through images on Flickr. In order to use this plugin you need to get an Api Key from Flickr and have the following software:]]></summary></entry><entry><title type="html">Bootstrap Theme Switcher jQuery plugin</title><link href="https://www.josephguadagno.net/2014/11/16/bootstrap-theme-switcher-jquery-plugin/" rel="alternate" type="text/html" title="Bootstrap Theme Switcher jQuery plugin" /><published>2014-11-15T19:43:00-07:00</published><updated>2014-11-15T19:43:00-07:00</updated><id>https://www.josephguadagno.net/2014/11/16/bootstrap-theme-switcher-jquery-plugin</id><content type="html" xml:base="https://www.josephguadagno.net/2014/11/16/bootstrap-theme-switcher-jquery-plugin/"><![CDATA[<p>A few months ago a published an article on a <a href="/2014/05/24/bootstrap-theme-switcher/">Bootstrap Theme Switcher</a>. I’ve since approved this post and the JavaScript and turned it into a jQuery plugin. This plugin works with the <a href="https://www.bootswatch.com">Bootswatch</a> {:target=”_blank”} <a href="https://bootswatch.com/help/#api" target="_blank">API</a> to provide the user with the following:</p>

<ul>
  <li>Loads a list of available themes from the API into a SELECT or a UL,</li>
  <li>Dynamically change the site theme to the selected theme</li>
  <li>Manually change the theme</li>
  <li>Save the selected theme to a cookie, requires the jQuery <a href="https://github.com/carhartl/jquery-cookie" target="_blank">Cookie</a> plugin,</li>
  <li>Loads the selected theme from a cookie, requires the jQuery <a href="https://github.com/carhartl/jquery-cookie" target="_blank">Cookie</a> plugin,</li>
</ul>

<p>The plugin also allows you to load a list of local themes in case you to not want to use the Bootswatch site.</p>

<p>Let me know what you think.</p>

<h2 id="downloads">Downloads</h2>

<table>
  <thead>
    <tr>
      <th>Download</th>
      <th>Link</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>jQuery Plugin page</td>
      <td><a href="https://plugins.jquery.com/bootstrapThemeSwitcher/" title="Bootstrap Theme Selector" target="_blank">https://plugins.jquery.com/bootstrapThemeSwitcher/</a></td>
    </tr>
    <tr>
      <td>GitHub Repository</td>
      <td><a href="https://github.com/jguadagno/bootstrapThemeSwitcher" title="https://github.com/jguadagno/bootstrapThemeSwitcher" target="_blank">https://github.com/jguadagno/bootstrapThemeSwitcher</a></td>
    </tr>
    <tr>
      <td>Demo site</td>
      <td><a href="https://introtobootstrap.azurewebsites.net/" title="Introduction to Twitter Bootstrap" target="_blank">https://introtobootstrap.azurewebsites.net/</a></td>
    </tr>
  </tbody>
</table>

<h3 id="next-steps">Next steps</h3>

<ul>
  <li>NuGet Package</li>
  <li>NPM module</li>
  <li>Bower module</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Bootstrap" /><category term="jQuery" /><category term="jQuery Plugin" /><category term="Plugin" /><category term="Theme" /><category term="Twitter Bootstrap" /><summary type="html"><![CDATA[A few months ago a published an article on a Bootstrap Theme Switcher. I’ve since approved this post and the JavaScript and turned it into a jQuery plugin. This plugin works with the Bootswatch {:target=”_blank”} API to provide the user with the following:]]></summary></entry><entry><title type="html">Taking the “Leap” to Everleap</title><link href="https://www.josephguadagno.net/2014/06/12/taking-the-leap-to-everleap/" rel="alternate" type="text/html" title="Taking the “Leap” to Everleap" /><published>2014-06-12T09:30:00-07:00</published><updated>2014-06-12T09:30:00-07:00</updated><id>https://www.josephguadagno.net/2014/06/12/taking-the-leap-to-everleap</id><content type="html" xml:base="https://www.josephguadagno.net/2014/06/12/taking-the-leap-to-everleap/"><![CDATA[<p>For the past 5+ years, I’ve had a few different sites hosted at <a href="https://www.discountasp.net/a/jguadagn" target="_blank">Discount ASP.NET</a>, this site being one of them. Recently, Discount ASP.NET released a new product in the web hosting arena, <a href="https://www.everleap.com/" target="_blank">Everleap</a>. Everleap is a new cloud-based hosting solution.I know, the word “cloud” is massively overused nowadays.Everleap differs from other solutions as it uses the <a href="https://www.everleap.com/cloud-hosting/features/azure-pack-hosting/" target="_blank">Windows Azure Pack</a> for its services. The Windows Azure Pack is the same technology that Microsoft uses to run its own Windows Azure hosting platform. Microsoft has made Azure technologies available to their partners so they can build public and private cloud offerings that run in their own data centers. For more on the Windows Azure Pack check out <a href="https://www.everleap.com/cloud-hosting/features/azure-pack-hosting/" title="https://www.everleap.com/cloud-hosting/features/azure-pack-hosting/" target="_blank">https://www.everleap.com/cloud-hosting/features/azure-pack-hosting/</a></p>

<h2 id="the-move-to-everleap">The move to Everleap</h2>

<p>The move to Everleap was easy considering I already had my site on Discount ASP.NET</p>

<ol>
  <li>Open up an Everleap account: <a href="https://www.everleap.com/sign-up/?plan=plan1" target="_blank">Cloud Websites</a> or <a href="https://www.everleap.com/sign-up/?plan=plan2" target="_blank">Multi-Domain Cloud</a></li>
  <li>Create a Discount ASP.NET support ticket requesting the move</li>
  <li>In a few hours or so, the new sites were created and moved over. They even updated web.config with the new database connection strings and SMTP settings.</li>
  <li>If you do not use Discount ASP.NET to register your domains, you will have to log in to your registrar to update the DNS servers to point to the new Everleap DNS servers.</li>
</ol>

<p>That was it.</p>

<p>If you need a scalable site where you can increase and decrease the number of servers as needed and want predictable billing, Everleap might be the solution for you.</p>

<p class="notice--info"><strong>Disclaimer</strong>: I was given a free Everleap account to host this site and other community sites that I run like <a href="https://www.desertcodecamp.com" target="_blank">Desert Code Camp</a>. This is not a sponsored post.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Web" /><category term="hosting" /><summary type="html"><![CDATA[For the past 5+ years, I’ve had a few different sites hosted at Discount ASP.NET, this site being one of them. Recently, Discount ASP.NET released a new product in the web hosting arena, Everleap. Everleap is a new cloud-based hosting solution.I know, the word “cloud” is massively overused nowadays.Everleap differs from other solutions as it uses the Windows Azure Pack for its services. The Windows Azure Pack is the same technology that Microsoft uses to run its own Windows Azure hosting platform. Microsoft has made Azure technologies available to their partners so they can build public and private cloud offerings that run in their own data centers. For more on the Windows Azure Pack check out https://www.everleap.com/cloud-hosting/features/azure-pack-hosting/]]></summary></entry><entry><title type="html">Book Review: Designing Evolvable Web APIs with ASP.NET</title><link href="https://www.josephguadagno.net/2014/05/27/book-review-designing-evolvable-web-apis-with-asp-net/" rel="alternate" type="text/html" title="Book Review: Designing Evolvable Web APIs with ASP.NET" /><published>2014-05-26T22:57:03-07:00</published><updated>2014-05-26T22:57:03-07:00</updated><id>https://www.josephguadagno.net/2014/05/27/book-review-designing-evolvable-web-apis-with-asp-net</id><content type="html" xml:base="https://www.josephguadagno.net/2014/05/27/book-review-designing-evolvable-web-apis-with-asp-net/"><![CDATA[<p>When I first learned about this book I could not wait to get my hands on it for two reasons.  One, my current work project involves designing an API for an application to “talk” to a wide variety of systems. The second is one of the authors, <a href="https://codebetter.com/glennblock/" target="_blank">Glenn Block</a>. I’ve known Glenn for a while. I’ve seen him present, I’ve talked with him, read his blog posts and knew the book would not disappoint and it did not.</p>

<p>The book starts off with covering the <a href="https://en.wikipedia.org/wiki/HTTP" target="_blank">HTTP</a> protocol, then moves into the different types of API styles like the <a href="https://martinfowler.com/articles/richardsonMaturityModel.html" target="_blank">Richardson Maturity Model,</a> <a href="https://en.wikipedia.org/wiki/Remote_procedure_call" target="_blank">RPC</a>, <a href="https://en.wikipedia.org/wiki/REST" target="_blank">REST</a> and others. The authors do this to give the readers an understanding of how the web works and why the <a href="https://www.asp.net/web-api" target="_blank">Web API</a> framework in ASP.NET is the way it is. The authors describe, in depth, building out an API, versioning it, applying security, formatting responses and talk about testability of an API.</p>

<p>In short, this book is a must have for anyone working on or designing any API.</p>

<p class="notice"><strong>Disclaimer</strong> I was given a copy of this book in the hopes that I would review it. I only post reviews on books that I think would provide some benefit to the development community.</p>

<p>Get <a href="https://www.amazon.com/gp/product/1449337716/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1449337716&amp;linkCode=as2&amp;tag=beyondthebasic0e&amp;linkId=ZQBIH5TTOE2YWAZP" target="_blank">Designing Evolvable Web APIs with ASP.NET</a> on Amazon.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[When I first learned about this book I could not wait to get my hands on it for two reasons.  One, my current work project involves designing an API for an application to “talk” to a wide variety of systems. The second is one of the authors, Glenn Block. I’ve known Glenn for a while. I’ve seen him present, I’ve talked with him, read his blog posts and knew the book would not disappoint and it did not.]]></summary></entry><entry><title type="html">Book Review: Visual Studio 2013 Cookbook</title><link href="https://www.josephguadagno.net/2014/05/26/book-review-visual-studio-2013-cookbook/" rel="alternate" type="text/html" title="Book Review: Visual Studio 2013 Cookbook" /><published>2014-05-26T11:22:05-07:00</published><updated>2014-05-26T11:22:05-07:00</updated><id>https://www.josephguadagno.net/2014/05/26/book-review-visual-studio-2013-cookbook</id><content type="html" xml:base="https://www.josephguadagno.net/2014/05/26/book-review-visual-studio-2013-cookbook/"><![CDATA[<p>When I first looked at the title of the book Visual Studio 2013 Cookbook I wondered what it could be about.  You can’t cook with Visual Studio, at least not yet :smile: . It turns out that, outside of two chapters, the book is an series of getting started guides with topics the are new(ish) to Visual Studio 2013. It is not a bad thing. If you are getting started with using Visual Studio 2013 or Team Foundation Server or .NET 4.5.x, this book provides a good overview of the new features in Visual Studio and version 4.5.x of the .NET Framework.</p>

<p class="notice"><strong>Disclaimer</strong>: I was given a copy of this book in the hopes that I would review it. I only post reviews on books that I think would provide some benefit to the development community.</p>

<p>Get <a href="https://www.amazon.com/gp/product/1782171967/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1782171967&amp;linkCode=as2&amp;tag=beyondthebasic0e&amp;linkId=E366A2OH33HLPZLY" target="_blank">Visual Studio 2013 Cookbook</a> on Amazon.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[When I first looked at the title of the book Visual Studio 2013 Cookbook I wondered what it could be about.  You can’t cook with Visual Studio, at least not yet :smile: . It turns out that, outside of two chapters, the book is an series of getting started guides with topics the are new(ish) to Visual Studio 2013. It is not a bad thing. If you are getting started with using Visual Studio 2013 or Team Foundation Server or .NET 4.5.x, this book provides a good overview of the new features in Visual Studio and version 4.5.x of the .NET Framework.]]></summary></entry><entry><title type="html">Book Review: NuGet 2 Essentials</title><link href="https://www.josephguadagno.net/2014/05/26/book-review-nuget-2-essentials/" rel="alternate" type="text/html" title="Book Review: NuGet 2 Essentials" /><published>2014-05-26T09:49:04-07:00</published><updated>2014-05-26T09:49:04-07:00</updated><id>https://www.josephguadagno.net/2014/05/26/book-review-nuget-2-essentials</id><content type="html" xml:base="https://www.josephguadagno.net/2014/05/26/book-review-nuget-2-essentials/"><![CDATA[<p>A few months ago I was asked to be a Technical Reviewer for a new NuGet book coming out.  I happily said yes and a month or two later out came <a href="https://www.amazon.com/gp/product/178216586X/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=178216586X&amp;linkCode=as2&amp;tag=beyondthebasic0e&amp;linkId=IK4FKY7RGOQT7LZU" target="_blank">NuGet 2 Essentials</a>.</p>

<p>The book starts off with the typical “<em>Hello World</em>” or in it’s case, the getting started with NuGet.  The authors do a good job of explaining what NuGet is, how to install it and how to use it to manage packages. They then go into great detail on the anatomy of a NuGet package, how to create, how to version, how to publish and how to add features to a package after it is deployed.  The final chapter is all about setting up your own NuGet server in your environment and how you can integrate NuGet into your continuous build process.</p>

<p>Overall, it is a good and quick read.  I find it a nice hand reference for when I am working on NuGet packages.</p>

<p>Get <a href="https://www.amazon.com/gp/product/178216586X/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=178216586X&amp;linkCode=as2&amp;tag=beyondthebasic0e&amp;linkId=IK4FKY7RGOQT7LZU" target="_blank">NuGet 2 Essentials</a> on Amazon.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[A few months ago I was asked to be a Technical Reviewer for a new NuGet book coming out.  I happily said yes and a month or two later out came NuGet 2 Essentials.]]></summary></entry><entry><title type="html">Bootstrap Theme Switcher</title><link href="https://www.josephguadagno.net/2014/05/24/bootstrap-theme-switcher/" rel="alternate" type="text/html" title="Bootstrap Theme Switcher" /><published>2014-05-23T21:07:00-07:00</published><updated>2014-05-23T21:07:00-07:00</updated><id>https://www.josephguadagno.net/2014/05/24/bootstrap-theme-switcher</id><content type="html" xml:base="https://www.josephguadagno.net/2014/05/24/bootstrap-theme-switcher/"><![CDATA[<p><strong>UPDATE</strong>: This is now a jQuery Plugin. See the post <a href="/2014/11/16/bootstrap-theme-switcher-jquery-plugin/">Bootstrap Theme Switcher jQuery Plugin</a>. While creating a Proof of Concept design for a web site I was working on I thought it would be cool to demonstrate the power of themes in Twitter <a href="https://getbootstrap.com" target="_blank">Bootstrap</a> by providing an option in the Proof of Concept to switch to some of the themes that are on <a href="https://www.bootswatch.com" target="_blank">Bootswatch</a>.  It was a remarkably easy solution, that involved <a href="https://www.jquery.com" target="_blank">jQuery</a>, <a href="https://github.com/carhartl/jquery-cookie" target="_blank">jQuery.cookie</a> plugin, Twitter Bootstrap <a href="https://getbootstrap.com/components/#navbar" target="_blank">Navbar</a> and the themes from <a href="https://www.bootswatch.com" target="_blank">Bootswatch</a>. This sample assumes that you are using a CDN for the Twitter Bootstrap and jQuery files and that you downloaded the themes from Bootswatch and copied them to the path of <code class="language-plaintext highlighter-rouge">/css/themes</code>. The example could easily be modified to use the themes on the Bootstrap <a href="https://www.bootstrapcdn.com" target="_blank">CDN</a>. First, let’s start with a basic template for Twitter Bootstrap</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;head&gt;</span>
        <span class="c">&lt;!-- Bootstrap core CSS --&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span>
            <span class="na">href=</span><span class="s">"https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span>
            <span class="na">href=</span><span class="s">"https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">id=</span><span class="s">"bootstrapTheme"</span> <span class="na">rel=</span><span class="s">"stylesheet"</span>
            <span class="na">href=</span><span class="s">"https://netdna.bootstrapcdn.com/bootswatch/3.1.1/cerulean/bootstrap.min.css"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span>
            <span class="na">href=</span><span class="s">"https://netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css"</span><span class="nt">&gt;</span>
        <span class="c">&lt;!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --&gt;</span>   
        <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
        <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
    <span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>

<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container-fluid"</span><span class="nt">&gt;</span>

Your content here!

<span class="nt">&lt;/div&gt;</span>

<span class="c">&lt;!-- jQuery (necessary for Bootstrap's JavaScript plugins) --&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"</span>
    <span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/script&gt;</span>
<span class="c">&lt;!-- Include all compiled plugins (below), or include individual files as needed --&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"</span>
    <span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">&gt;</span>
<span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"/js/site.js"</span> <span class="na">type=</span><span class="s">"text/javascript"</span><span class="nt">&gt;&lt;/script&gt;</span>

<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Next, we’ll add the navbar with the theme selector menu.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"navbar-header"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;button</span> <span class="na">class=</span><span class="s">"navbar-toggle"</span><span class="nt">&gt;</span> 
      <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"sr-only"</span><span class="nt">&gt;</span>Toggle navigation<span class="nt">&lt;/span&gt;</span>
    <span class="nt">&lt;/button&gt;</span>
      <span class="nt">&lt;a</span> <span class="na">class=</span><span class="s">"navbar-brand"</span> <span class="na">href=</span><span class="s">"index.html"</span><span class="nt">&gt;</span>Intro to Twitter Bootstrap<span class="nt">&lt;/a&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"collapse navbar-collapse"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">"nav navbar-nav"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"dropdown"</span><span class="nt">&gt;&lt;a</span> <span class="na">class=</span><span class="s">"dropdown-toggle"</span> <span class="na">href=</span><span class="s">"#"</span><span class="nt">&gt;</span>Bootstrap Site <span class="nt">&lt;/a&gt;</span>
      <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">"dropdown-menu"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>default<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>amelia<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>cerulean<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>cosmo<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>custom<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>cyborg<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>darkly<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>flatly<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>journal<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>lumen<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>readable<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>shamrock<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>simplex<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>slate<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>spacelab<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>superhero<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>united<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;a&gt;</span>yeti<span class="nt">&lt;/a&gt;&lt;/li&gt;</span>
      <span class="nt">&lt;/ul&gt;</span>
    <span class="nt">&lt;/li&gt;</span>
  <span class="nt">&lt;/ul&gt;</span>
  <span class="nt">&lt;/div&gt;</span><span class="c">&lt;!--/.nav-collapse --&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then, the JavaScript</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre><span class="nx">$</span><span class="p">().</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="cm">/* For theme switching */</span>
  <span class="kd">var</span> <span class="nx">themeName</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">cookie</span><span class="p">(</span><span class="dl">"</span><span class="s2">themeName</span><span class="dl">"</span><span class="p">);</span>
  <span class="kd">var</span> <span class="nx">themePath</span> <span class="o">=</span> <span class="nx">$</span><span class="p">.</span><span class="nx">cookie</span><span class="p">(</span><span class="dl">"</span><span class="s2">themePath</span><span class="dl">"</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">themeName</span> <span class="o">!==</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">setTheme</span><span class="p">(</span><span class="nx">themeName</span><span class="p">,</span> <span class="nx">themePath</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">});</span>

<span class="kd">function</span> <span class="nx">setTheme</span><span class="p">(</span><span class="nx">themeName</span><span class="p">,</span> <span class="nx">themePath</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">cssLink</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
  <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#bootstrapTheme</span><span class="dl">'</span><span class="p">).</span><span class="nx">replaceWith</span><span class="p">(</span><span class="nx">cssLink</span><span class="p">);</span>

  <span class="nx">$</span><span class="p">.</span><span class="nx">cookie</span><span class="p">(</span><span class="dl">"</span><span class="s2">themeName</span><span class="dl">"</span><span class="p">,</span> <span class="nx">themeName</span><span class="p">,</span> <span class="p">{</span> <span class="na">expires</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span> <span class="na">path</span><span class="p">:</span> <span class="dl">"</span><span class="s2">/</span><span class="dl">"</span> <span class="p">});</span>
  <span class="nx">$</span><span class="p">.</span><span class="nx">cookie</span><span class="p">(</span><span class="dl">"</span><span class="s2">themePath</span><span class="dl">"</span><span class="p">,</span> <span class="nx">themePath</span><span class="p">,</span> <span class="p">{</span> <span class="na">expires</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span> <span class="na">path</span><span class="p">:</span> <span class="dl">"</span><span class="s2">/</span><span class="dl">"</span> <span class="p">});</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now you are done. But how does it work? In the head section of the template you should notice that we gave one of the stylesheets an id of <code class="language-plaintext highlighter-rouge">bootstrapTheme</code>. This link will be modified by the <code class="language-plaintext highlighter-rouge">setTheme</code> function in the JavaScript file to dynamically change the theme and save the theme preference to a cookie to load in the future. In the jQuery <code class="language-plaintext highlighter-rouge">ready</code> function, we check for the presence of the cookie <code class="language-plaintext highlighter-rouge">themeName</code>, if present, a call is made to <code class="language-plaintext highlighter-rouge">setTheme</code> to load the theme. The theme is changed by clicking on one of theme menu items which, in turn, calls <code class="language-plaintext highlighter-rouge">setTheme</code>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Bootstrap" /><category term="jQuery" /><category term="jQuery Plugin" /><category term="Plugin" /><category term="Twitter Bootstrap" /><summary type="html"><![CDATA[UPDATE: This is now a jQuery Plugin. See the post Bootstrap Theme Switcher jQuery Plugin. While creating a Proof of Concept design for a web site I was working on I thought it would be cool to demonstrate the power of themes in Twitter Bootstrap by providing an option in the Proof of Concept to switch to some of the themes that are on Bootswatch.  It was a remarkably easy solution, that involved jQuery, jQuery.cookie plugin, Twitter Bootstrap Navbar and the themes from Bootswatch. This sample assumes that you are using a CDN for the Twitter Bootstrap and jQuery files and that you downloaded the themes from Bootswatch and copied them to the path of /css/themes. The example could easily be modified to use the themes on the Bootstrap CDN. First, let’s start with a basic template for Twitter Bootstrap]]></summary></entry><entry><title type="html">Obsessed With Finding a Better Way</title><link href="https://www.josephguadagno.net/2014/04/26/obsessed-with-finding-a-better-way/" rel="alternate" type="text/html" title="Obsessed With Finding a Better Way" /><published>2014-04-25T17:08:34-07:00</published><updated>2014-04-25T17:08:34-07:00</updated><id>https://www.josephguadagno.net/2014/04/26/obsessed-with-finding-a-better-way</id><content type="html" xml:base="https://www.josephguadagno.net/2014/04/26/obsessed-with-finding-a-better-way/"><![CDATA[<p>For those that are friends, follow this blog or follow me on <a href="https://www.twitter.com/jguadagno" target="_blank">Twitter</a>, know that about 9 months ago I started working at <a href="https://www.rocketmortgage.com" target="_blank">Rocket Mortgage</a> as a Software Architect thanks to a friend of mine <a href="https://keithelder.net/" target="_blank">Keith Elder.</a>  That is not the reason for the post, however.  At Rocket Mortgage, part of the ideas we live by at all of the family companies is our “<strong><a href="https://www.rocketmortgage.com/press-room/fast-facts/our-isms/" target="_blank">ISMs</a></strong>”. One of the ISMs is <em>Obsessed with finding a better way</em>, The ISM is defined as</p>

<blockquote>
  <p><strong>Our mission is a never-ending quest to find a better way</strong> for every process and everything we touch. If it’s good, let’s make it great. If it’s great, let’s take it to an even higher level. Don’t settle for less. In fact, don’t settle at all. ‘Finding a better way’ is not something we do on the side or when we get time. Rather, it’s a key responsibility for every one of our team members. It’s our passion… our way of living…our obsession.</p>
</blockquote>

<p>For the last few weeks, I have been transforming the <a href="https://www.desertcodecamp.com" target="_blank">Desert Code Camp</a> site into a site that I can white box. I’ve been working on this for two reasons. The first to use for the annual Rocket Mortgage technology conference, and the second, so that other conference organizers can use it for their conference(s).  As I was working on converting/improving the site, I noticed my self saying/thinking, “<em>why the heck did I do it this way</em>”, or “<em>OMG, this is terrible</em>”.  In most cases, it was either because that is how I knew how to do it, or that was the process or technology available to me at the time.</p>

<p>The reason is not what’s important.  The important part was me recognizing that there is a better way to do what I was trying to do, not different, but <em>better</em>.  As a technology professional, I hope that as time goes by and technology improves, us technology professionals improve with it.  This means that we do not do it a certain way because that is what we know and are comfortable with but we try to improve what we worked on and make it better.</p>

<p>Getting back to the conversion, I probably could have finished the conversion in a few days but it has taken me almost a month (mostly nights) to do the conversion.  Reason beings are that I am <em>obsessed with finding a better way</em>. This is not solely because I work at Rocket Mortgage but because I am always looking for ways to improve processes, applications and most of all myself.</p>

<p>So I urge you, as a software engineer, test engineer, project manager, or human being, be <strong><em>obsessed with finding a better way</em></strong> in your career and life in general.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Personal" /><category term="Desert Code Camp" /><summary type="html"><![CDATA[For those that are friends, follow this blog or follow me on Twitter, know that about 9 months ago I started working at Rocket Mortgage as a Software Architect thanks to a friend of mine Keith Elder. That is not the reason for the post, however. At Rocket Mortgage, part of the ideas we live by at all of the family companies is our “ISMs”. One of the ISMs is Obsessed with finding a better way, The ISM is defined as]]></summary></entry><entry><title type="html">Twitter Bootstrap Resources</title><link href="https://www.josephguadagno.net/2014/01/24/twitter-bootstrap-resources/" rel="alternate" type="text/html" title="Twitter Bootstrap Resources" /><published>2014-01-24T06:50:30-07:00</published><updated>2014-01-24T06:50:30-07:00</updated><id>https://www.josephguadagno.net/2014/01/24/twitter-bootstrap-resources</id><content type="html" xml:base="https://www.josephguadagno.net/2014/01/24/twitter-bootstrap-resources/"><![CDATA[<p>This does Over the past few months, I have built quite a few sites using Twitter <a href="https://www.getbootstrap.com" target="_blank">Bootstrap</a>. I think it is an awesome framework for styling websites and abstracts away a lot of the differences in CSS between modern browsers.  I say Twitter Bootstrap did for CSS what <a href="https://www.jquery.com" target="_blank">jquery</a> did for JavaScript. I typically say that if you use both frameworks in the same site, your site will work pretty consistent against all modern browsers. Anyway, the purpose of this post was to list all of the Twitter Bootstrap resources that use or find helpful.</p>

<ul>
  <li><a href="#content-delivery-networks-cdn">Content Delivery Networks (CDN)</a></li>
  <li><a href="#resource-guides">Resource Guides</a></li>
  <li><a href="#tutorials">Tutorials</a></li>
  <li><a href="#themes">Themes</a></li>
  <li><a href="#layout-helpers">Layout Helpers</a></li>
  <li><a href="#visual-studio-nuget-packages">Visual Studio NuGet Packages</a></li>
  <li><a href="#ui-widgets">UI Widgets</a>
    <ul>
      <li><a href="#general-libraries">General Libraries</a></li>
      <li><a href="#buttons">Buttons</a></li>
      <li><a href="#fonts">Fonts</a></li>
      <li><a href="#input-widgets">Input Widgets</a></li>
    </ul>
  </li>
</ul>

<h2 id="content-delivery-networks-cdn">Content Delivery Networks (CDN)</h2>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bootstrap CDN</td>
      <td><a href="https://www.bootstrapcdn.com" target="_blank">https://www.bootstrapcdn.com</a></td>
    </tr>
    <tr>
      <td>Microsoft Ajax CDN</td>
      <td><a href="https://www.asp.net/ajaxlibrary/cdn.ashx#Bootstrap_Releases_on_the_CDN_14" title="https://www.asp.net/ajaxlibrary/cdn.ashx#Bootstrap_Releases_on_the_CDN_14" target="_blank">https://www.asp.net/ajaxlibrary/cdn.ashx#Bootstrap_Releases_on_the_CDN_14</a></td>
    </tr>
  </tbody>
</table>

<h2 id="resource-guides">Resource Guides</h2>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>The Big Bad Ass List of Twitter Bootstrap Resources</td>
      <td><a href="https://www.bootstraphero.com/the-big-badass-list-of-twitter-bootstrap-resources" title="https://www.bootstraphero.com/the-big-badass-list-of-twitter-bootstrap-resources" target="_blank">https://www.bootstraphero.com/the-big-badass-list-of-twitter-bootstrap-resources</a></td>
    </tr>
  </tbody>
</table>

<h2 id="tutorials">Tutorials</h2>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bootstrap Tutorial: A Guide for Beginners</td>
      <td><a href="https://blog.udemy.com/bootstrap-tutorial-a-guide-for-beginners/" title="Bootstrap Tutorial: A Guide for Beginners" target="_blank">https://blog.udemy.com/bootstrap-tutorial-a-guide-for-beginners/</a></td>
    </tr>
  </tbody>
</table>

<h2 id="themes">Themes</h2>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bootswatch</td>
      <td><a href="https://bootswatch.com/" title="https://bootswatch.com/" target="_blank">https://bootswatch.com/</a></td>
    </tr>
    <tr>
      <td>Themes for Bootstrap</td>
      <td><a href="https://www.themesforbootstrap.com/" title="https://www.themesforbootstrap.com/" target="_blank">https://www.themesforbootstrap.com/</a></td>
    </tr>
    <tr>
      <td>jquery-ui for Bootstrap</td>
      <td><a href="https://github.com/jquery-ui-bootstrap/jquery-ui-bootstrap/" title="https://github.com/jquery-ui-bootstrap/jquery-ui-bootstrap/" target="_blank">https://github.com/jquery-ui-bootstrap/jquery-ui-bootstrap/</a></td>
    </tr>
    <tr>
      <td>Style Bootstrap</td>
      <td><a href="https://stylebootstrap.info/" title="https://stylebootstrap.info/" target="_blank">https://stylebootstrap.info/</a></td>
    </tr>
    <tr>
      <td>PaintStrap</td>
      <td><a href="https://paintstrap.com/" title="https://paintstrap.com/" target="_blank">https://paintstrap.com/</a></td>
    </tr>
  </tbody>
</table>

<h2 id="layout-helpers">Layout Helpers</h2>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bootply</td>
      <td><a href="https://www.bootply.com" target="_blank">https://www.bootply.com</a></td>
    </tr>
    <tr>
      <td>BootUI</td>
      <td><a href="https://www.bootui.com/" title="https://www.bootui.com/" target="_blank">https://www.bootui.com/</a></td>
    </tr>
    <tr>
      <td>Layoutit</td>
      <td><a href="https://www.layoutit.com/build" title="https://www.layoutit.com/build" target="_blank">https://www.layoutit.com/build</a></td>
    </tr>
  </tbody>
</table>

<h2 id="visual-studio-nuget-packages">Visual Studio NuGet Packages</h2>

<p>As of <a href="https://www.asp.net/mvc/mvc5" target="_blank">ASP.NET MVC 5</a>, the File | New templates are already wired for Twitter Bootstrap, just in case, here are a few packages to get you started.</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bootstrap</td>
      <td><a href="https://www.nuget.org/packages/bootstrap/" title="https://www.nuget.org/packages/bootstrap/" target="_blank">https://www.nuget.org/packages/bootstrap/</a></td>
    </tr>
    <tr>
      <td>AngularJS UI Bootstrap</td>
      <td><a href="https://www.nuget.org/packages/Angular.UI.Bootstrap/" title="https://www.nuget.org/packages/Angular.UI.Bootstrap/" target="_blank">https://www.nuget.org/packages/Angular.UI.Bootstrap/</a></td>
    </tr>
    <tr>
      <td>Bootstrap for MVC 5</td>
      <td><a href="https://www.nuget.org/packages/Twitter.Bootstrap.MVC/" title="https://www.nuget.org/packages/Twitter.Bootstrap.MVC/" target="_blank">https://www.nuget.org/packages/Twitter.Bootstrap.MVC/</a></td>
    </tr>
    <tr>
      <td>Paged List for ASP.NET (Bootstrap)</td>
      <td><a href="https://www.nuget.org/packages/PagedListExt/" title="https://www.nuget.org/packages/PagedListExt/" target="_blank">https://www.nuget.org/packages/PagedListExt/</a></td>
    </tr>
    <tr>
      <td>Font Awesome</td>
      <td><a href="https://www.nuget.org/packages/FontAwesome/" title="https://www.nuget.org/packages/FontAwesome/" target="_blank">https://www.nuget.org/packages/FontAwesome/</a></td>
    </tr>
  </tbody>
</table>

<h2 id="ui-widgets">UI Widgets</h2>

<ul>
  <li><a href="#general-libraries">General Libraries</a></li>
  <li><a href="#buttons">Buttons</a></li>
  <li><a href="#fonts">Fonts</a></li>
  <li><a href="#input-widgets">Input Widgets</a></li>
</ul>

<h3 id="general-libraries">General Libraries</h3>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bootstrap Components (for ASP.NET MVC)</td>
      <td><a href="https://bootstrapcomponents.apphb.com/" title="https://bootstrapcomponents.apphb.com/" target="_blank">https://bootstrapcomponents.apphb.com/</a></td>
    </tr>
    <tr>
      <td>Fuel UX</td>
      <td><a href="https://exacttarget.github.io/fuelux/#" title="https://exacttarget.github.io/fuelux/#" target="_blank">https://exacttarget.github.io/fuelux/#</a></td>
    </tr>
  </tbody>
</table>

<h3 id="buttons">Buttons</h3>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Button Builder</td>
      <td><a href="https://bootsnipp.com/buttons" title="https://bootsnipp.com/buttons" target="_blank">https://bootsnipp.com/buttons</a></td>
    </tr>
  </tbody>
</table>

<h3 id="fonts">Fonts</h3>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Font Awesome</td>
      <td><a href="https://fontawesome.io/" title="https://fontawesome.io/" target="_blank">https://fontawesome.io/</a></td>
    </tr>
    <tr>
      <td>Font Custom</td>
      <td><a href="https://fontcustom.com/" title="https://fontcustom.com/" target="_blank">https://fontcustom.com/</a></td>
    </tr>
  </tbody>
</table>

<h3 id="input-widgets">Input Widgets</h3>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bootstrap Form Helpers</td>
      <td><a href="https://bootstrapformhelpers.com/" title="https://bootstrapformhelpers.com/" target="_blank">https://bootstrapformhelpers.com/</a></td>
    </tr>
    <tr>
      <td>Bootstrap Acknowledge Inputs</td>
      <td><a href="https://averagemarcus.github.com/Bootstrap-AcknowledgeInputs/" title="https://averagemarcus.github.com/Bootstrap-AcknowledgeInputs/" target="_blank">https://averagemarcus.github.com/Bootstrap-AcknowledgeInputs/</a></td>
    </tr>
    <tr>
      <td>Bootstrap Wysiwyg Editor</td>
      <td><a href="https://mindmup.github.io/bootstrap-wysiwyg/" title="https://mindmup.github.io/bootstrap-wysiwyg/" target="_blank">https://mindmup.github.io/bootstrap-wysiwyg/</a></td>
    </tr>
    <tr>
      <td>Bootstrap Validation Helpers</td>
      <td><a href="https://reactiveraven.github.io/jqBootstrapValidation/" title="https://reactiveraven.github.io/jqBootstrapValidation/" target="_blank">https://reactiveraven.github.io/jqBootstrapValidation/</a></td>
    </tr>
    <tr>
      <td>Date Picker</td>
      <td><a href="https://www.eyecon.ro/bootstrap-datepicker/" title="https://www.eyecon.ro/bootstrap-datepicker/" target="_blank">https://www.eyecon.ro/bootstrap-datepicker/</a></td>
    </tr>
    <tr>
      <td>Time Picker</td>
      <td><a href="https://github.com/jdewit/bootstrap-timepicker" title="https://github.com/jdewit/bootstrap-timepicker" target="_blank">https://github.com/jdewit/bootstrap-timepicker</a></td>
    </tr>
  </tbody>
</table>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Bootstrap" /><category term="Twitter Bootstrap" /><summary type="html"><![CDATA[This does Over the past few months, I have built quite a few sites using Twitter Bootstrap. I think it is an awesome framework for styling websites and abstracts away a lot of the differences in CSS between modern browsers. I say Twitter Bootstrap did for CSS what jquery did for JavaScript. I typically say that if you use both frameworks in the same site, your site will work pretty consistent against all modern browsers. Anyway, the purpose of this post was to list all of the Twitter Bootstrap resources that use or find helpful.]]></summary></entry><entry><title type="html">Adding Visual Studio Online to an Windows Azure MSDN Benefit Subscription</title><link href="https://www.josephguadagno.net/2013/11/15/adding-visual-studio-online-to-an-windows-azure-msdn-benefit-subscription/" rel="alternate" type="text/html" title="Adding Visual Studio Online to an Windows Azure MSDN Benefit Subscription" /><published>2013-11-15T06:21:37-07:00</published><updated>2013-11-15T06:21:37-07:00</updated><id>https://www.josephguadagno.net/2013/11/15/adding-visual-studio-online-to-an-windows-azure-msdn-benefit-subscription</id><content type="html" xml:base="https://www.josephguadagno.net/2013/11/15/adding-visual-studio-online-to-an-windows-azure-msdn-benefit-subscription/"><![CDATA[<p>In my previous post where I talk about the <a href="/2013/11/15/visual-studio-online-with-windows-azure-msdn-benefit/">Visual Studio Online with Microsoft Azure MSDN benefits</a>,  I talk about why you can not add Visual Studio Online to an Existing Windows Azure MSDN benefit subscription.  In this post, I will show you how to add and existing Visual Studio Online (formally know as TFS Online) to your existing Windows Azure account.</p>

<h2 id="create-your-pay-as-you-go-account">Create Your “Pay-As-You-Go” Account</h2>

<p>If you already have a “Pay-As-You-Go” account you can jump to the “Link <a href="/2013/11/15/visual-studio-online-with-windows-azure-msdn-benefit/">Your Existing Visual Studio Online Account</a>” section First you have to sign in to your Windows Azure <a href="https://account.windowsazure.com/Subscriptions" target="_blank">account</a>. You should see something like this. <a href="/assets/images/posts/Add_subscription.png"><img src="/assets/images/posts/Add_subscription_thumb.png" alt="Add subscription" title="Add subscription" /></a> Click on the Add Subscription button. This will take you to the Add Subscription page. You should see a few choices, scroll down until you see the “Pay-As-You-Go” selection highlighted below. <a href="/assets/images/posts/Select_an_Offer_1.png"><img src="/assets/images/posts/Select_an_Offer_thumb_1.png" alt="Select an Offer" title="Select an Offer" /></a> You should then be brought to the purchase section, <a href="/assets/images/posts/purchase_1.png"><img src="/assets/images/posts/purchase_thumb_1.png" alt="purchase" title="purchase" /></a> <em>Note, you might have to validate your mobile number and/or put in a credit card number</em>.  Click purchase, and after a minute or so, your “Pay-As-You-Go” account is established.  You can view it by clicking on the Portal button or visiting the Windows Azure <a href="https://portal.windowsazure.com/" target="_blank">Portal</a>.</p>

<h2 id="linking-your-existing-visual-studio-online-account">Linking Your Existing Visual Studio Online Account</h2>

<p>Now that you have the “Pay-As-You-Go” account added to Windows Azure, we can click on the Visual Studio Online tab in the Windows Azure portal <a href="/assets/images/posts/Visual_Studio_Online.png"><img src="/assets/images/posts/Visual_Studio_Online_thumb.png" alt="Visual Studio Online" title="Visual Studio Online" /></a> or click the new button. <a href="/assets/images/posts/Link_to_Existing_Account.png"><img src="/assets/images/posts/Link_to_Existing_Account_thumb.png" alt="Link to Existing Account" title="Link to Existing Account" /></a> If you click, the new button you will have to navigate to “App Services”, then “Visual Studio Online”, then “Link to Existing”. For me, since my Windows Azure credentials were the same as my Visual Studio Online credentials, it automatically populated the account name.  Click the button and the Link to Existing Account message will appear. <a href="/assets/images/posts/Link_Being_Created.png"><img src="/assets/images/posts/Link_Being_Created_thumb.png" alt="Link Being Created" title="Link Being Created" /></a> Once the account has been linked you will see the account ready screen. <a href="/assets/images/posts/Account_Ready.png"><img src="/assets/images/posts/Account_Ready_thumb.png" alt="Account Ready" title="Account Ready" /></a> Now, from the Windows Azure dashboard, you will see your Visual Studio Online account. <a href="/assets/images/posts/With_Visual_Studio_Online.png"><img src="/assets/images/posts/With_Visual_Studio_Online_thumb.png" alt="With Visual Studio Online" title="With Visual Studio Online" /></a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Microsoft Azure" /><category term="Visual Studio" /><category term="Visual Studio Online" /><category term="Windows Azure" /><summary type="html"><![CDATA[In my previous post where I talk about the Visual Studio Online with Microsoft Azure MSDN benefits, I talk about why you can not add Visual Studio Online to an Existing Windows Azure MSDN benefit subscription. In this post, I will show you how to add and existing Visual Studio Online (formally know as TFS Online) to your existing Windows Azure account.]]></summary></entry><entry><title type="html">Visual Studio Online with Windows Azure MSDN benefit</title><link href="https://www.josephguadagno.net/2013/11/15/visual-studio-online-with-windows-azure-msdn-benefit/" rel="alternate" type="text/html" title="Visual Studio Online with Windows Azure MSDN benefit" /><published>2013-11-15T05:52:34-07:00</published><updated>2013-11-15T05:52:34-07:00</updated><id>https://www.josephguadagno.net/2013/11/15/visual-studio-online-with-windows-azure-msdn-benefit</id><content type="html" xml:base="https://www.josephguadagno.net/2013/11/15/visual-studio-online-with-windows-azure-msdn-benefit/"><![CDATA[<p>I don’t know about you but when I watched the Visual Studio launch earlier this week I was really excited about some of the cool new features added. The first thing I wanted to try out was <a href="Visual Studio Online">Visual Studio Online</a>. I figured I have a Windows Azure account set up through my MSDN subscription so I’d be set.  Unfortunately, like most people found out when they tried to add their Visual Studio Online account they could not.  We were all getting this message…</p>

<p>You have no <a href="https://go.microsoft.com/fwLink/?LinkID=317720&amp;clcid=0x409" target="_blank">eligible Windows Azure subscriptions</a>. To buy monthly user licenses or shared resources for your Visual Studio Online account, you’ll need another <a href="https://go.microsoft.com/fwLink/?LinkID=328562&amp;clcid=0x409" target="_blank">Windows Azure subscription</a>.</p>

<p>I went to the Windows Azure account portal to make sure that I did have an active subscription, in fact, I have two, but I still could not add or link my existing Visual Studio Online account.  I posted a message on one of the Windows Azure list and after a few hours someone pointed me to this <a href="https://social.msdn.microsoft.com/Forums/vstudio/en-US/1176969f-7389-4e6b-937e-20b17726487f/visual-studio-online-eligibility?forum=TFService&amp;WT.mc_id=DT-MVP-4024623" target="_blank">forum post</a>. This forum post explains why you can not add a Visual Studio Online account to an existing Windows Azure MSDN account.  It essentially boils down to billing.  The Windows Azure MSDN benefit has the ability to “cap” the spending in Windows Azure.  This does not work for Visual Studio Online. If you think about it, it makes sense. Do you not want to lose access to your source code because you hit your spending limit? Probably not.  In order to add your Visual Studio Online account, you have to get create a Windows Azure “Pay as you Go” subscription.  Don’t worry, you get a bunch of the benefits <a href="https://www.windowsazure.com/en-us/pricing/details/visual-studio-online/" target="_blank">free</a>.</p>

<blockquote>
  <p>Within a Visual Studio Online account, you simply pay for user plans for the users who join your account and for resources that are shared amongst all users on the account. The first five users with the Basic plan and all eligible MSDN subscribers (Visual Studio Professional with MSDN and above) can join your account at no charge. <a href="https://go.microsoft.com/fwlink/?linkid=328238&amp;clcid=0x409" target="_blank">Learn more about Visual Studio with MSDN</a>.</p>
</blockquote>

<p>Hopefully, this helps.  My next post will talk about <a href="/2013/11/15/adding-visual-studio-online-to-an-windows-azure-msdn-benefit-subscription/">Adding Visual Studio Online to a Windows Azure MSDN benefit</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Microsoft Azure" /><category term="Visual Studio" /><category term="Visual Studio Online" /><category term="Windows Azure" /><summary type="html"><![CDATA[I don’t know about you but when I watched the Visual Studio launch earlier this week I was really excited about some of the cool new features added. The first thing I wanted to try out was Visual Studio Online. I figured I have a Windows Azure account set up through my MSDN subscription so I’d be set. Unfortunately, like most people found out when they tried to add their Visual Studio Online account they could not. We were all getting this message…]]></summary></entry><entry><title type="html">Windows Azure Experience–Part 4: Customizing the Web Site</title><link href="https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-4-customizing-the-web-site/" rel="alternate" type="text/html" title="Windows Azure Experience–Part 4: Customizing the Web Site" /><published>2013-05-23T00:52:00-07:00</published><updated>2013-05-23T00:52:00-07:00</updated><id>https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-4-customizing-the-web-site</id><content type="html" xml:base="https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-4-customizing-the-web-site/"><![CDATA[<p>This Part 4 of a <a href="/2013/05/21/the-windows-azure-experience/">series of posts</a> about my “upgrade” to Windows Azure. Next up in the series is customizing the Windows Azure <a href="https://www.windowsazure.com/en-us/manage/services/web-sites/" target="_blank">Web Site</a>.</p>

<p class="notice--info"><strong>Note:</strong> This blog post is based on the Preview version of Windows Azure Web Sites and might change in the future.</p>

<p class="notice--info"><strong>Note:</strong> There is an <a href="https://www.windowsazure.com/en-us/manage/services/web-sites/how-to-configure-websites/" target="_blank">article</a> on the Windows Azure site that will cover some of this content.</p>

<h2 id="getting-started">Getting Started</h2>

<p>When you first come to you web site from the management portal you are presented with a screen similar to this. <a href="/assets/images/posts/image_21.png"><img src="/assets/images/posts/image_thumb_20.png" alt="image" title="image" /></a> The start page gives you three options:</p>

<h3 id="get-the-tools">Get the tools</h3>

<p>WebMatrix is a free, lightweight web development tool that includes everything you need to create web sites and publish applications for Windows Azure. WebMatrix supports the latest web technologies, including ASP.NET, PHP, HTML5, CSS3, Node and more. The Windows Azure SDKs allow you to build applications that take advantage of Windows Azure’s scalable cloud computing resources.</p>

<h4 id="web-matrix">Web Matrix</h4>

<p>Clicking on this link will redirect you to the latest and greatest version of <a href="https://www.microsoft.com/web/webmatrix/" target="_blank">WebMatrix</a>.</p>

<h4 id="install-a-windows-azure-sdk">Install a Windows Azure SDK</h4>

<p>Clicking on this link will redirect your to the downloads page for all of the <a href="https://www.windowsazure.com/en-us/downloads/?fb=en-us" target="_blank">Windows Azure SDKs</a>. There are SDKs available for Mobile development, including Android, iOS, Windows Phone 8, and Windows Store. Here are some of the others</p>

<ul>
  <li>.NET: Visual Studio 2010, Visual Studio 2012</li>
  <li>Node.js</li>
  <li>PHP</li>
  <li>Java</li>
  <li>Python</li>
  <li>Ruby</li>
  <li>Media libraries and player frameworks.</li>
</ul>

<h3 id="publish-your-app">Publish your app</h3>

<p>After you set up a web site and dependent resources, such as a database, you can download the generated publish profile, import it into a development tool such as WebMatrix or Visual Web Developer, and deploy your web site to Windows Azure within seconds. You can also publish your web application from FTP directly by setting up deployment credentials in the portal and pushing the application to Windows Azure from your favorite FTP client. There will be more on this in a future blog post.</p>

<h3 id="integrate-source-control">Integrate source control</h3>

<p>Set up continuous deployment from source control providers like TFS, CodePlex, GitHub, Dropbox, or Bitbucket. You can also deploy from a local Git repository on your machine. There will be more on this in a future blog post.</p>

<h2 id="dashboard">Dashboard</h2>

<h3 id="monitor">Monitor</h3>

<p>The dashboard allows you to see some performance metrics of your site. These are CPU Time, Data In, Data Out, HTTP Server Errors, and Requests. <a href="/assets/images/posts/image_22.png"><img src="/assets/images/posts/image_thumb_21.png" alt="image" title="image" /></a>  </p>

<h3 id="usage-overview">Usage Overview</h3>

<p>The usage overview shows you where you might be in trouble of hitting the limits of the the type of site you have (free, shared, reserved). <a href="/assets/images/posts/image_23.png"><img src="/assets/images/posts/image_thumb_22.png" alt="image" title="image" /></a>  </p>

<h3 id="quick-glance">Quick Glance</h3>

<p>This shows you every thing that you will need to connect to your Windows Azure services. <a href="/assets/images/posts/image_24.png"><img src="/assets/images/posts/image_thumb_23.png" alt="image" title="image" /></a></p>

<h2 id="monitor-1">Monitor</h2>

<p>The monitor displays the some of the web sites performance counters like CPU time, Data In, Data Out, etc. <a href="/assets/images/posts/image_25.png"><img src="/assets/images/posts/image_thumb_24.png" alt="image" title="image" /></a></p>

<h2 id="configure">Configure</h2>

<h3 id="general">General</h3>

<p>Choose the version of .NET language runtime to use. The current supported version is v.3.5 and v4.5 Choose the version of PHP to use. The current supported versions are 5.3 and 5.4.</p>

<h3 id="domain-names">Domain Names</h3>

<p>Lists the domain names that are associated with your account. If you have a Shared or Reserved instance, you can click on Manage domains to add or remove domains or subdomains.</p>

<h3 id="app-diagnostics">App Diagnostics</h3>

<p>You can turn on any of the application diagnostics like application logging to the file system and application logging to a Windows Azure Storage account.</p>

<h3 id="site-diagnostics">Site Diagnostics</h3>

<p>You can turn on any of the three site wide diagnostics settings like Web Server Logging, Detailed Error Messages, and Failed Request Tracing.</p>

<h3 id="monitoring">Monitoring</h3>

<p>Endpoint monitoring lets you monitor the availability of HTTP or HTTPS endpoints from geo-distributed locations. You can test an endpoint from up to three geo-distributed locations. A monitoring test fails if the HTTP response code is greater than or equal to 400 or if the response takes more than 30 seconds. An endpoint is considered available if its monitoring tests succeed from all the specified locations.</p>

<h3 id="app-settings">App Settings</h3>

<p>Specify name/value pairs that will be loaded by your web application when it starts.</p>

<h3 id="connection-strings">Connection Strings</h3>

<p>Show any connection strings associated with linked resources.</p>

<h3 id="default-documents">Default Documents</h3>

<p>Use this setting to add, remove, or reorder your web site’s default documents.</p>

<h3 id="handler-mappings">Handler Mappings</h3>

<p>Specify a custom script processor to handle requests for the file extension that you specified. To do this, provide a path for the custom script. The path must be relative to the FTP root directory of the web site. You can use optional arguments.</p>

<h2 id="scale">Scale</h2>

<h3 id="general-1">General</h3>

<p>Windows Azure offers three modes for you to run your web sites: Free, Shared, and Reserved. In the Free and Shared modes, all web sites run in a multi-tenant environment and have quotas for usage of CPU, memory, and network resources. You can decide which sites you want to run in Free mode and which sites you want to run in Shared mode. Shared mode employs less stringent resource usage quotas than Free mode. The maximum number of sites you can run in Free mode may vary with your plan. When you choose Reserved mode, all your web sites run in Reserved mode on dedicated virtual machines that correspond to standard Windows Azure compute resources. <strong>Note:</strong> <em>This can affect the cost of running your web site on Windows Azure.</em></p>

<h3 id="capacity">Capacity</h3>

<p>This count denotes the number of processes dedicated to a web site. By increasing the value for this setting, you can scale your web site for additional throughput and availability. <strong>Note:</strong> <em>This can affect the cost of running your web site on Windows Azure.</em></p>

<h2 id="linked-resources">Linked Resources</h2>

<p>This will show you a list of other Windows Azure Services that you have linked or attached to the web site, like a database.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Microsoft Azure" /><category term="Windows Azure" /><summary type="html"><![CDATA[This Part 4 of a series of posts about my “upgrade” to Windows Azure. Next up in the series is customizing the Windows Azure Web Site.]]></summary></entry><entry><title type="html">Windows Azure Experience–Part 3: Creating the Web Site</title><link href="https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-3-creating-the-web-site/" rel="alternate" type="text/html" title="Windows Azure Experience–Part 3: Creating the Web Site" /><published>2013-05-23T00:04:00-07:00</published><updated>2013-05-23T00:04:00-07:00</updated><id>https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-3-creating-the-web-site</id><content type="html" xml:base="https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-3-creating-the-web-site/"><![CDATA[<p>This Part 3 of a <a href="/2013/05/21/the-windows-azure-experience/">series of posts</a> about my “upgrade” to Windows Azure. Next up in the series is creating the Windows Azure <a href="https://www.windowsazure.com/en-us/manage/services/web-sites/" target="_blank">Web Site</a>.</p>

<p class="notice--info"><strong>Note:</strong> This blog post is based on the Preview version of Windows Azure Web Sites and might, change in the future. To get create a new Web Site from the management portal you simply need to click on the <strong>+ New</strong> button.</p>

<h2 id="creating-the-web-site">Creating the Web Site</h2>

<p><a href="/assets/images/posts/image_12.png"><img src="/assets/images/posts/image_thumb_11.png" alt="image" title="image" /></a> From here a menu will appear. <em>Hint: if you are in the Web Sites section of the Table of Contents, you will get three new web site selections available, otherwise you will have to navigate “Compute” then “Web Site”</em> <a href="/assets/images/posts/image_13.png"><img src="/assets/images/posts/image_thumb_12.png" alt="image" title="image" /></a> You’ll see that I have 3 options <strong>Quick Create</strong> Quickly create your web site by specifying a URL. You can perform tasks like deployment and configuration later. <strong>Custom Create</strong> Create a web site with additional options, such as a new or existing database, or with continuous deployment from source control. <strong>From Gallery</strong> Choose a web application from the gallery. This lets you choose from one of the many preconfigured ASP.NET applications like WordPress.</p>

<h3 id="quick-create">Quick Create</h3>

<p>Click on Quick Create and you will be asked to enter a URL and Region for your website like this.</p>

<p><a href="/assets/images/posts/image_14.png" class="align-center"><img src="/assets/images/posts/image_thumb_13.png" alt="image-center" title="image" /></a></p>

<p>Whether you are choosing a Free, Shared or Reserved web site you must select the subdomain to use with .azurewebsites.net.  The site will do a real time check to see if you have chosen a name that already exists like this.</p>

<p><a href="/assets/images/posts/image_15.png" class="align-center"><img src="/assets/images/posts/image_thumb_14.png" alt="image-center" title="image" /></a></p>

<p>If the subdomain you have chosen is valid and available the text box will look like this.</p>

<p><a href="/assets/images/posts/image_16.png" class="align-center"><img src="/assets/images/posts/image_thumb_15.png" alt="image-center" title="image" /></a> If your region is good, you can click on Create Web Site. Unfortunately, I could not get a screenshot to show the status will it was creating but this is what the page looks like after.</p>

<p><a href="/assets/images/posts/image_17.png" class="align-center"><img src="/assets/images/posts/image_thumb_16.png" alt="image-center" title="image" /></a></p>

<p>You’ll notice now that I have a second web site created, in like 10 seconds. with a notification telling me it was complete.</p>

<h3 id="custom-create">Custom Create</h3>

<p>For custom complete, you are asked for a little bit more information. <a href="/assets/images/posts/image_18.png"><img src="/assets/images/posts/image_thumb_17.png" alt="image" title="image" /></a></p>

<p><strong>URL</strong>: Must be a subdomain that is valid an available</p>

<p><strong>Region:</strong> Chose a region close to where your primary users will be.</p>

<p><strong>Database:</strong> Select from the following choices <a href="/assets/images/posts/image_19.png" class="align-right"><img src="/assets/images/posts/image_thumb_18.png" alt="image-right" title="image" /></a> If you have existing databases, you can select one of them.  If you select Create a new SQL Server database or Create a new MySQL database, you will get prompted for more details around that database. <strong>Note:</strong> <em>If the web site region and database region do not match, you will get an alert similar to this.</em>
<a href="/assets/images/posts/image_20.png"><img src="/assets/images/posts/image_thumb_19.png" alt="image" title="image" /></a></p>

<p><strong>Publish from source control:</strong> If you select this option, you have the ability to chose from one of many options including:</p>

<ul>
  <li>Team Foundation Service</li>
  <li>CodePlex</li>
  <li>GitHub</li>
  <li>DropBox</li>
  <li>Bitbucket</li>
  <li>External repository</li>
  <li>Local Git Repository</li>
</ul>

<p>Each one of these will take to a login page for that service where you can authorize access to the service.</p>

<h3 id="from-the-gallery">From the Gallery</h3>

<p>For the Gallery, you get to choose from a wide selection of prebuilt sites and templates. These are broken up into categories to make the selection easier.  Here are some (not all) of the out of the cloud templates:</p>

<ul>
  <li>DotNetNuke</li>
  <li>Acquia Drupal</li>
  <li>ASP.NET Empty Site</li>
  <li>BlogEngine.NET (my favorite)</li>
  <li>DasBlog</li>
  <li>Joomla</li>
  <li>Orchard</li>
  <li>Umbraco</li>
  <li>WordPress</li>
  <li>and many more….</li>
</ul>

<p>Once you choose one, there will be prompts for the specific site you choose to install.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Microsoft Azure" /><category term="Windows Azure" /><summary type="html"><![CDATA[This Part 3 of a series of posts about my “upgrade” to Windows Azure. Next up in the series is creating the Windows Azure Web Site.]]></summary></entry><entry><title type="html">Windows Azure Experience–Part 2: Management Portal</title><link href="https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-2-management-portal/" rel="alternate" type="text/html" title="Windows Azure Experience–Part 2: Management Portal" /><published>2013-05-22T23:07:00-07:00</published><updated>2013-05-22T23:07:00-07:00</updated><id>https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-2-management-portal</id><content type="html" xml:base="https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-2-management-portal/"><![CDATA[<p>This Part 2 of a <a href="/2013/05/21/the-windows-azure-experience/">series of posts</a> about my “upgrade” to Windows Azure. Next up in this series is the Windows Azure Management Portal.  It can be accessed directly at <a href="https://manage.windowsazure.com/" title="https://manage.windowsazure.com/" target="_blank">https://manage.windowsazure.com/</a> or by visiting <a href="https://www.windowsazure.com/en-us/" target="_blank">https://www.windowsazure.com/en-us/</a> and clicking on the portal link in the upper hand corner. <a href="/assets/images/posts/SNAGHTMLba8532e.png"><img src="/assets/images/posts/SNAGHTMLba8532e_thumb.png" alt="SNAGHTMLba8532e[5]" title="SNAGHTMLba8532e[5]" /></a>   To access the portal you will need to sign in with the same Windows Live account that you used to create the account.</p>

<p class="notice--info"><strong><em>Note:</em></strong> There are two different sign is screens available, one for those the use a Microsoft Account for Office 365 and one that use a “regular” Windows Live account like hotmail.com or msn.com.</p>

<h2 id="navigation">Navigation</h2>

<p>The “home page” for the portal is broken up into 4 parts: <a href="/assets/images/posts/image_10.png"><img src="/assets/images/posts/image_thumb_9.png" alt="image" title="image" /></a>  </p>

<h3 id="part-1---navigation-bar">Part 1 - Navigation Bar</h3>

<p>The navigation bar allows you to view documentation, pricing, downloads, community and support. You can also change the language and access your account.</p>

<h3 id="part-2--table-of-contents">Part 2 – “Table of Contents”</h3>

<p>This lets you access off of the Windows Azure Services that you have.  There will be a number beneath each services which indicates what have many “items” you have activated with that service.  In my example, I have 1 Web Site and 2 SQL Databases.  You can click on any of these to access the service.</p>

<h3 id="part-3--main-body">Part 3 – Main Body</h3>

<p>This is where the settings or actions of the service items live.  In the example above, you see all items which lists all of the services I have and their current status. For the “All Items” selection you get a list of all the services that you have a subscription and their statuses.  Windows Azure allows you to have multiple subscriptions linked to one account, that is why the subscription column is present. <a href="/assets/images/posts/image_11.png"><img src="/assets/images/posts/image_thumb_10.png" alt="image" title="image" /></a></p>

<h3 id="part-4--app-bar">Part 4 – App Bar</h3>

<p>The app bar is synonymous with the  Windows 8 app bar, it provides actions that you can take based on what service you are in and what item is selected. In the example above I have a web site selected so I am presented with the ability to:</p>

<ul>
  <li>Create a new Web Site</li>
  <li>Browse the Site</li>
  <li>Stop the Site</li>
  <li>Restart the Site</li>
  <li>Manage Domains</li>
  <li>Delete the Site</li>
  <li>Install Web Matrix</li>
  <li>View (context sensitive) Help</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Microsoft Azure" /><category term="Windows Azure" /><summary type="html"><![CDATA[This Part 2 of a series of posts about my “upgrade” to Windows Azure. Next up in this series is the Windows Azure Management Portal.  It can be accessed directly at https://manage.windowsazure.com/ or by visiting https://www.windowsazure.com/en-us/ and clicking on the portal link in the upper hand corner.   To access the portal you will need to sign in with the same Windows Live account that you used to create the account.]]></summary></entry><entry><title type="html">Windows Azure Experience–Part 1: Creating the Account</title><link href="https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-1-creating-the-account/" rel="alternate" type="text/html" title="Windows Azure Experience–Part 1: Creating the Account" /><published>2013-05-22T22:18:00-07:00</published><updated>2013-05-22T22:18:00-07:00</updated><id>https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-1-creating-the-account</id><content type="html" xml:base="https://www.josephguadagno.net/2013/05/23/windows-azure-experience-part-1-creating-the-account/"><![CDATA[<p>This Part 1 of a <a href="/2013/05/21/the-windows-azure-experience/">series of posts</a> about my “upgrade” to Windows Azure.</p>

<p>After I decided to embark on this adventure, I activated my “<a href="https://www.windowsazure.com/en-us/pricing/member-offers/msdn-benefits/" target="_blank">Azure Benefit</a>” which was part of my Microsoft MVP benefit. However, most users will want to navigate to the <a href="https://www.windowsazure.com/en-us/pricing/overview/" target="_blank">pricing overview</a> page and see what services you require based on your needs. In order for me to determine what services I needed I went through the <a href="https://www.windowsazure.com/en-us/documentation/" target="_blank">documentation</a> on the Windows Azure portal. I decided that for my needs I needed a <a href="https://www.windowsazure.com/en-us/manage/services/web-sites/">Web Site</a> {:target=”_blank”}(shared) and one <a href="https://www.windowsazure.com/en-us/manage/services/sql-databases/" target="_blank">SQL Database</a>. I went with the <em>shared</em> web site, instead of the using one of the 10 <a href="https://www.windowsazure.com/en-us/pricing/details/web-sites/" target="_blank">free</a> because I wanted a custom domain.  If you are fine with <em>yourdomain</em>.azurewebsites.net, then the free web sites might be an option.</p>

<h2 id="getting-started">Getting Started</h2>

<p>You have three choices when you want to use Windows Azure</p>

<ul>
  <li>Free Trial (<a href="https://www.windowsazure.com/en-us/pricing/free-trial/" target="_blank">https://www.windowsazure.com/en-us/pricing/free-trial/</a>)</li>
  <li>Buy Now (<a href="https://www.windowsazure.com/en-us/pricing/purchase-options/" title="https://www.windowsazure.com/en-us/pricing/purchase-options/" target="_blank">https://www.windowsazure.com/en-us/pricing/purchase-options/</a>)</li>
  <li>10 Free Web Sites (<a href="https://www.windowsazure.com/en-us/pricing/details/web-sites/" target="_blank">https://www.windowsazure.com/en-us/pricing/details/web-sites/</a>)</li>
</ul>

<p>No matter what option you chose, you will need a Windows Azure account (any Windows Live Id will do) and a valid credit card, even for the free stuff. The credit card will only be charged for what you use over your preset spending limit that you set in your management portal. If you are curious as to how much it will cost you, you can use the handy <a href="https://www.windowsazure.com/en-us/pricing/calculator/" target="_blank">calculator</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Microsoft Azure" /><category term="Windows Azure" /><summary type="html"><![CDATA[This Part 1 of a series of posts about my “upgrade” to Windows Azure.]]></summary></entry><entry><title type="html">The Windows Azure Experience</title><link href="https://www.josephguadagno.net/2013/05/21/the-windows-azure-experience/" rel="alternate" type="text/html" title="The Windows Azure Experience" /><published>2013-05-20T21:48:00-07:00</published><updated>2013-05-20T21:48:00-07:00</updated><id>https://www.josephguadagno.net/2013/05/21/the-windows-azure-experience</id><content type="html" xml:base="https://www.josephguadagno.net/2013/05/21/the-windows-azure-experience/"><![CDATA[<p>A few weeks ago, I embarked on a task to move one of my sites from a traditional hosting provider to <a href="https://www.windowsazure.com/en-us/" target="_blank">Windows Azure</a> <a href="https://www.windowsazure.com/en-us/home/scenarios/web-sites/" target="_blank">Web Sites</a>. I did this for a few reasons and it has <strong>nothing</strong> to do with the hosting provider.</p>

<ol>
  <li>I was helping out at one of Global Windows Azure Boot Camps, so I felt I should see what it’s like first hand.</li>
  <li>The site I moved needed to be able to scale up on certain days/weeks.</li>
  <li>I’m a geek and wanted a new “toy” to play with.</li>
</ol>

<p>This blog post is the first in a few that will talk about the different parts of the overall “Migration”</p>

<ul>
  <li><a href="/2013/05/23/windows-azure-experience-part-1-creating-the-account/">Part 1: Creating the Account</a></li>
  <li><a href="/2013/05/23/windows-azure-experience-part-2-management-portal/">Part 2: Management Portal</a></li>
  <li><a href="/2013/05/23/windows-azure-experience-part-3-creating-the-web-site/">Part 3: Creating the Web Site</a></li>
  <li><a href="/2013/05/23/windows-azure-experience-part-4-customizing-the-web-site/">Part 4: Customizing the Web Site</a></li>
  <li>Part 5: The Database</li>
  <li>Part 6: Publishing</li>
  <li>Part 7: Subdomains</li>
  <li>Part 8: Monitoring</li>
</ul>

<p>The “Parts” will be updated as the posts are published.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Azure" /><category term="Microsoft Azure" /><category term="Windows Azure" /><summary type="html"><![CDATA[A few weeks ago, I embarked on a task to move one of my sites from a traditional hosting provider to Windows Azure Web Sites. I did this for a few reasons and it has nothing to do with the hosting provider.]]></summary></entry><entry><title type="html">ASP.NET FriendlyUrls ViewSwitcher and jQuery Mobile</title><link href="https://www.josephguadagno.net/2013/05/21/asp-net-friendlyurls-viewswitcher-and-jquery-mobile/" rel="alternate" type="text/html" title="ASP.NET FriendlyUrls ViewSwitcher and jQuery Mobile" /><published>2013-05-20T18:55:27-07:00</published><updated>2013-05-20T18:55:27-07:00</updated><id>https://www.josephguadagno.net/2013/05/21/asp-net-friendlyurls-viewswitcher-and-jquery-mobile</id><content type="html" xml:base="https://www.josephguadagno.net/2013/05/21/asp-net-friendlyurls-viewswitcher-and-jquery-mobile/"><![CDATA[<p>Sometime ago, September of 2012, <a href="https://www.hanselman.com/" target="_blank">Scott Hanselman</a> blogged about a cool new library written for ASP.NET WebForms called <a href="https://aspnetfriendlyurls.codeplex.com/" target="_blank">ASP.NET Friendly URLs</a>. The ASP.NET Friendly URLs library makes it easy to enable extensionless URLs for file-based handlers (e.g. ASPX, ASHX) in ASP.NET applications.  The library itself is very cool. In a nutshell, you, with two lines of code, can have extensionless URLs in ASP.NET web forms and have different pages served up for mobile devices.  Scott Hanselman’s blog post covers all of the details on features and implementation. I wanted to show you a problem that I had with using the library, namely the ViewSwitcher control, with <a href="https://www.jquerymobile.com" target="_blank">jQuery Mobile</a>.</p>

<p>I followed the thorough instructions on Scott Hanselman’s blog post about how to enable or create the mobile version of the site. Essentially you have to create a <code class="language-plaintext highlighter-rouge">**&lt;MasterPageName&gt;**.Mobile.Master</code> and then a <code class="language-plaintext highlighter-rouge">**&lt;PageName&gt;**.Mobile.aspx</code> for each content page that you want a mobile version for.  So in my case I created a SiteMaster.Mobile.aspx, a Default.Mobile.aspx and a EventInfo.Mobile.aspx.  Now when anyone browses the default or EventInfo page of my site with a Mobile device that ASP.NET detects, it will show that page instead and keep the URL the same.  If you want to see it action, visit <a href="https://teched2013.techedevents.info" target="_blank">https://teched2013.techedevents.info</a> from a desktop or mobile device.  The creators of the ASP.NET Friendly URL library also created a helpful little control called ViewSwitcher.  The ViewSwitcher control allows the end user of the site to switch between the “desktop” view of a page and the “mobile” view of the page.  To add it, all you have to do is register the control on the page, and place it where you want on the page.</p>

<h2 id="sitemaster-and-sitemobilemaster">Site.Master and Site.Mobile.Master</h2>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;</span><span class="err">%@</span> <span class="na">Register</span> <span class="na">Src=</span><span class="s">"~/ViewSwitcher.ascx"</span> <span class="na">TagPrefix=</span><span class="s">"friendlyUrls"</span> <span class="na">TagName=</span><span class="s">"ViewSwitcher&gt;
&lt;!– Add this where you want the control displayed –/&gt;
&lt;friendlyUrls:ViewSwitcher ID="</span><span class="na">ViewSwitcher1</span><span class="err">"</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Once these two lines of markup are on your Master Page, the FriendlyUrls library will generate a block of HTML enabling the user to switch views, if the page has an alternate view.</p>

<p>This works great out of the box if you are not using jQuery Mobile and the reason for it is not the FriendlyUrls library but the way jQuery Mobile tries to load pages via <a href="https://jquerymobile.com/demos/1.2.0/docs/pages/page-navmodel.html" target="_blank">Ajax</a>. If you run your application “out of the box” while on a jQuery Mobile based view, and try to switch to Desktop view you will get redirected to [https://domainname/<em>FriendlyUrls_SwitchView?ReturnUrl=/](https://domainname/_FriendlyUrls_SwitchView?ReturnUrl=/){:target=”_blank”},</em> or something similar. This is because the FriendlyUrl libraries changes the view using a cookie and the jQuery Mobile framework only attempts to load the changes for the page (not the entire page) so there is a little trouble.  No worries there is a quick fix. If you open up the ViewSwitcher.ascx control and add the following attribute to the anchor tag</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nx">data</span><span class="o">-</span><span class="nx">ajax</span><span class="o">=</span><span class="dl">"</span><span class="s2">false</span><span class="dl">"</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This tells jQuery Mobile to not load that page with Ajax and load the entire page.</p>

<p>Ideally when the library hits the ASP.NET open source stack (its on its way), I or someone else will contribute this to the control to implement this small fix.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Technology" /><category term="ASP.NET" /><category term="jQuery Mobile" /><summary type="html"><![CDATA[Sometime ago, September of 2012, Scott Hanselman blogged about a cool new library written for ASP.NET WebForms called ASP.NET Friendly URLs. The ASP.NET Friendly URLs library makes it easy to enable extensionless URLs for file-based handlers (e.g. ASPX, ASHX) in ASP.NET applications.  The library itself is very cool. In a nutshell, you, with two lines of code, can have extensionless URLs in ASP.NET web forms and have different pages served up for mobile devices.  Scott Hanselman’s blog post covers all of the details on features and implementation. I wanted to show you a problem that I had with using the library, namely the ViewSwitcher control, with jQuery Mobile.]]></summary></entry><entry><title type="html">Visual Studio 2012–Update Issues</title><link href="https://www.josephguadagno.net/2013/04/05/visual-studio-2012-update-issues/" rel="alternate" type="text/html" title="Visual Studio 2012–Update Issues" /><published>2013-04-05T09:12:35-07:00</published><updated>2013-04-05T09:12:35-07:00</updated><id>https://www.josephguadagno.net/2013/04/05/visual-studio-2012-update-issues</id><content type="html" xml:base="https://www.josephguadagno.net/2013/04/05/visual-studio-2012-update-issues/"><![CDATA[<p>Yesterday I decided to re-pave my personal development machine because it was acting crazy.  Since Visual Studio 2012 <a href="https://www.microsoft.com/en-us/download/details.aspx?id=38188" target="_blank">Update 2</a> came out on that day, it was a no brainer for me to install Visual Studio 2012 and then apply Visual Studio 2012 Update 2.  All of the installations when fine until I tried applying the Visual Studio 2012 Update 2.  It keeps on failing, and I mean like 15 times.  I tried running under administrative user, with/without antivirus, I tried downloading the “offline” version 1 .   However, NOTHING worked.  I kept getting an error reporting that the package <strong>vc_runtimeMinimum_x64</strong> was failing.  I tried downloading the lasted version of it from Microsoft downloads but that did not work. After reading a couple of threads on the <a href="https://social.msdn.microsoft.com/Forums/en-US/vssetup/threads?WT.mc_id=DT-MVP-4024623" target="_blank">Visual Studio Setup and Installation forums</a> and trying a few things I eventually got it to install. It did complain about a few different items not available but I did not have those products installed.</p>

<p>So what fixed, you asked? Windows Update!  More so I believe it was <a href="https://support.microsoft.com/kb/2781514/en-us" target="_blank">KB2781514</a> that did the trick. So you need to make sure your computer is totally updated to date with the latest updates for this to work.</p>

<p>Run from the command line vs2012.2.exe /layout to download all of the files at once.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Visual Studio" /><summary type="html"><![CDATA[Yesterday I decided to re-pave my personal development machine because it was acting crazy. Since Visual Studio 2012 Update 2 came out on that day, it was a no brainer for me to install Visual Studio 2012 and then apply Visual Studio 2012 Update 2. All of the installations when fine until I tried applying the Visual Studio 2012 Update 2. It keeps on failing, and I mean like 15 times. I tried running under administrative user, with/without antivirus, I tried downloading the “offline” version 1 . However, NOTHING worked. I kept getting an error reporting that the package vc_runtimeMinimum_x64 was failing. I tried downloading the lasted version of it from Microsoft downloads but that did not work. After reading a couple of threads on the Visual Studio Setup and Installation forums and trying a few things I eventually got it to install. It did complain about a few different items not available but I did not have those products installed.]]></summary></entry><entry><title type="html">2 Year Retrospective</title><link href="https://www.josephguadagno.net/2013/03/20/2-year-retrospective/" rel="alternate" type="text/html" title="2 Year Retrospective" /><published>2013-03-19T20:17:36-07:00</published><updated>2013-03-19T20:17:36-07:00</updated><id>https://www.josephguadagno.net/2013/03/20/2-year-retrospective</id><content type="html" xml:base="https://www.josephguadagno.net/2013/03/20/2-year-retrospective/"><![CDATA[<p>At around 6:30am PST on 3/19/2013, I hung up the phone from my last INETA North America Board meeting in which I was the President. I actually did not hit me until Nancy said, “Hey Joe, this is your last call as President” towards the end of the call. I was both relieved and saddened at the same time. I was relieved primarily because not everything was my ultimate responsibility anymore. I was saddened because I believe I had just started to gain momentum in making INETA more relevant again.</p>

<p>FYI, I did not lose an election or get fired or choose not run; the INETA Bylaws state that no officer (President, Vice President, Treasurer, or Secretary) can hold the position for two consecutive terms. I will stay on the INETA North America Board as Vice President underneath a good friend of mine, <a href="https://www.julieyack.com/" target="_blank">Julie Yack</a>, the next President of INETA North America.</p>

<p>Let’s go back in time to the Microsoft 2011 Global MVP Summit (February 2011), at which point I knew I won the election for President but the announcement was not public yet. I started out my pre-term talking to a lot of MVPs, speakers, former INETA Speaker Bureau speakers and former Board members asking them what they honestly thought about INETA, its benefits and programs. There were two common responses “INETA still exists” or “INETA is no longer relevant.” After thinking about this for a bit, I remember a lengthy conversation I had with a friend of mine, <a href="https://www.telerikwatch.com/" target="_blank">Todd Anglin</a>, in which Todd told me what INETA has done poorly in the past and what had worked. I remember telling Todd that I would be known as the President that “put the final nail in the coffin” and shut down INETA, or I would be known as the President that turned INETA around and made it relevant to user groups and the community again. I believe I have accomplished the latter.</p>

<p>After the Microsoft Global Summit, we, as a board, met over the phone and in person to come up with a strategy to “make INETA relevant again.” We began to really promote our <a href="https://ineta.org/speakers/" target="_blank">Community Speaker Program</a> as well as our <a href="https://ineta.org/CodeChallenge/default.aspx" target="_blank">Component Code Challenge</a> heavily. The Community Speaker Program continues to grow as we add more speakers and more events every month. INETA also started to participate more actively in conferences, like <a href="https://northamerica.msteched.com/#fbid=rSTkKfmHoDj" target="_blank">Microsoft TechEd North America</a>, <a href="https://devconnections.com/" target="_blank">DevConnections</a> and <a href="https://www.devintersection.com/?refer=JoeG" target="_blank">DevIntersections</a>, by having booths and side events like the INETA Community Leadership Summits.</p>

<p>In 2012, INETA turned the big One Oh (10) and we had a nice birthday celebration, we posted a <a href="https://blog.ineta.org/2012/02/default.aspx" target="_blank">blog</a> a day (almost) where we asked the community to submit posts about INETA and how it has affected them. My favorite post was the kick off <a href="https://blog.ineta.org/post/INETA-10-Happy-Birthday-INETA.aspx" target="_blank">one</a>. I wonder why :smile:. During that month, we also kicked off the INETA User Group Kits, which I thought were awesome.</p>

<p>After our birthday celebration month, we continued to heavily work on increasing our speakers and community events throughout the year and worked on improving the website. We also made a huge effort to change the way we handled and requested sponsorship. We decided to stop asking sponsors for money every time something came up and decided it was best to break up what we do into components and offer the typical and potential sponsors of INETA North America the ability to sponsor parts of INETA that make sense for them. For more on that, feel free to check out the <a href="https://ineta.org/Sponsors/ListSponsors.aspx" target="_blank">sponsorship</a> page.</p>

<p>Unfortunately, there is much more to do. We want to refresh the <a href="https://www.ineta.org/" target="_blank">INETA</a> website, improve the process flow for the Community Speakers program, and if possible, add some mobile clients to the mix.</p>

<p>So if INETA has touched you in some way or helped you, let me know (leave a comment here) and let other present or past Board members know. If you are willing to help in some way to make “INETA relevant again,” shoot me an email at joseph.guadagno (at) ineta.org.</p>

<p>And a HUGE thanks goes to the great team of <a href="https://ineta.org/BoardMember.aspx" target="_blank">individuals</a> that serve on the Board of Directors for INETA. It would not be a success without your hard work and dedication.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Community" /><category term="INETA" /><summary type="html"><![CDATA[At around 6:30am PST on 3/19/2013, I hung up the phone from my last INETA North America Board meeting in which I was the President. I actually did not hit me until Nancy said, “Hey Joe, this is your last call as President” towards the end of the call. I was both relieved and saddened at the same time. I was relieved primarily because not everything was my ultimate responsibility anymore. I was saddened because I believe I had just started to gain momentum in making INETA more relevant again.]]></summary></entry><entry><title type="html">Great Events in November 2012</title><link href="https://www.josephguadagno.net/2012/11/06/great-events-in-november-2012/" rel="alternate" type="text/html" title="Great Events in November 2012" /><published>2012-11-05T19:28:00-07:00</published><updated>2012-11-05T19:28:00-07:00</updated><id>https://www.josephguadagno.net/2012/11/06/great-events-in-november-2012</id><content type="html" xml:base="https://www.josephguadagno.net/2012/11/06/great-events-in-november-2012/"><![CDATA[<p>There are three great events coming to Phoenix this month.</p>

<ol>
  <li>Desert Code Camp on November 17th : <a href="https://nov2012.desertcodecamp.com" target="_blank">https://nov2012.desertcodecamp.com</a></li>
  <li>Windows 8 UX Design on November 26th: <a href="https://blogs.msdn.com/b/palermo4/archive/2012/11/06/2012-nov-26-windows-8-ux-design.aspx" target="_blank">https://blogs.msdn.com/b/palermo4/archive/2012/11/06/2012-nov-26-windows-8-ux-design.aspx</a></li>
  <li>Markus Egger presents on .NET and Windows 8 at the Microsoft Offices in Tempe from 1p-4:30p On November 26th. Registration: <a href="https://www.codemag.com/Training/Detail/fbcc732a-4d69-49f7-9313-b031ea80d9f3" target="_blank">https://www.codemag.com/Training/Detail/fbcc732a-4d69-49f7-9313-b031ea80d9f3</a> *</li>
  <li>Following that event, is Carl Franklin and Richard Campbell and the .NET Rocks tour from 6:00pm to 10:00pm. Registration: <a href="https://www.microsoft.com/click/services/Redirect2.ashx?CR_CC=200141036&amp;CR_EAC=300056117" target="_blank">https://www.microsoft.com/click/services/Redirect2.ashx?CR_CC=200141036&amp;CR_EAC=300056117</a></li>
</ol>

<p>You definitely need to attend these great training opportunities.</p>

<ul>
  <li>Dinner will provided in the middle of the Markus Egger presentation and the .NET Rocks tour.</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Arizona" /><category term="Community" /><category term="Events" /><summary type="html"><![CDATA[There are three great events coming to Phoenix this month.]]></summary></entry><entry><title type="html">Upcoming Events in Arizona</title><link href="https://www.josephguadagno.net/2012/05/03/upcoming-events-in-arizona/" rel="alternate" type="text/html" title="Upcoming Events in Arizona" /><published>2012-05-03T07:57:00-07:00</published><updated>2012-05-03T07:57:00-07:00</updated><id>https://www.josephguadagno.net/2012/05/03/upcoming-events-in-arizona</id><content type="html" xml:base="https://www.josephguadagno.net/2012/05/03/upcoming-events-in-arizona/"><![CDATA[<p>I was going through the <a href="https://www.azgroups.com" target="_blank">AZ Groups</a> calendar this week and noticed that we have a lot of events going on in Arizona in the next two related to Microsoft .NET Technologies.  Here are some of the notable ones.</p>

<table>
  <thead>
    <tr>
      <th>Date</th>
      <th>Title</th>
      <th>City</th>
      <th>More info</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>5/5</td>
      <td>Windows Phone Unleashed XNA</td>
      <td>Tempe, AZ</td>
      <td><a href="https://www.winphoneunleashed.com/register?ticketType=49ec85c5-afc2-4c92-91c4-cd73af3a9453" target="_blank">More Info</a></td>
    </tr>
    <tr>
      <td>5/12</td>
      <td>Windows Phone Unleaded Business Apps</td>
      <td>Chandler, AZ</td>
      <td><a href="https://www.winphoneunleashed.com/register?ticketType=0dd86188-b195-4c30-9285-969d81a89e18" target="_blank">More Info</a></td>
    </tr>
    <tr>
      <td>5/24</td>
      <td>Southeast Valley .NET User Group <em>Special Guest Speaker</em></td>
      <td>Chandler, AZ</td>
      <td><a href="https://sevdnug.org/Events/All/SEVDNUG_May_2012_Meeting_-_Intro_to_Prism.aspx" target="_blank">More Info</a></td>
    </tr>
    <tr>
      <td>5/25</td>
      <td>Microsoft Dev Camps Web</td>
      <td>Tempe, AZ</td>
      <td><a href="https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032506738&amp;culture=en-us" target="_blank">More Info</a></td>
    </tr>
    <tr>
      <td>6/6</td>
      <td>Phoenix Connected Systems User Group <em>Special Guest Speaker</em></td>
      <td>Tempe, AZ</td>
      <td><a href="https://pcsug.org/Home/Events" target="_blank">More Info</a></td>
    </tr>
    <tr>
      <td>6/8</td>
      <td>Microsoft Dev Camps Windows 8</td>
      <td>Chandler, AZ</td>
      <td><a href="https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032507678&amp;Culture=en-US" target="_blank">More Info</a></td>
    </tr>
    <tr>
      <td>6/9</td>
      <td>Microsoft Node Boot Camp</td>
      <td>Chandler, AZ</td>
      <td><a href="https://www.eventbrite.com/org/1908684919" target="_blank">More Info</a></td>
    </tr>
    <tr>
      <td>6/19</td>
      <td>AZGroups Day of .NET with <em>Scott Guthrie</em> and <em>Scott Hanselman</em></td>
      <td>Scottsdale, AZ</td>
      <td><a href="https://azgroups.eventday.com" target="_blank">More Info</a></td>
    </tr>
    <tr>
      <td>6/26</td>
      <td>Southeast Valley .NET User Group <em>Special Guest Speaker</em></td>
      <td>Chandler, AZ</td>
      <td><a href="https://sevdnug.org/events/all/SEVDNUG_June_2012_Meeting.aspx" target="_blank">More Info</a></td>
    </tr>
  </tbody>
</table>

<p>A lot effort is put into organizing these events and getting these “big” name speakers and content to come to Arizona, so show your support by attending these awesome free events.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Events" /><category term="Community" /><category term="Arizona" /><summary type="html"><![CDATA[I was going through the AZ Groups calendar this week and noticed that we have a lot of events going on in Arizona in the next two related to Microsoft .NET Technologies.  Here are some of the notable ones.]]></summary></entry><entry><title type="html">Windows Phone Unleashed Events</title><link href="https://www.josephguadagno.net/2012/04/11/windows-phone-unleashed-events/" rel="alternate" type="text/html" title="Windows Phone Unleashed Events" /><published>2012-04-11T10:19:22-07:00</published><updated>2012-04-11T10:19:22-07:00</updated><id>https://www.josephguadagno.net/2012/04/11/windows-phone-unleashed-events</id><content type="html" xml:base="https://www.josephguadagno.net/2012/04/11/windows-phone-unleashed-events/"><![CDATA[<p>There are two events Windows Phone Unleashed events coming to the Phoenix metropolitan area in May. Please register soon before the events sell out. Registration and prerequisites can be found next to each of the events.</p>

<ul>
  <li><a href="#game-developers">Game Development</a> by <a href="https://weblogs.asp.net/dwahlin/" target="_blank">Dan Wahlin</a> May 5th, Tempe, AZ. <a href="https://www.winphoneunleashed.com/register?ticketType=49ec85c5-afc2-4c92-91c4-cd73af3a9453" target="_blank">Register</a></li>
  <li><a href="#business-applications-developers" target="_blank">Business Application</a> Development by Joseph Guadagno, May 12th, Chandler, AZ. <a href="https://www.winphoneunleashed.com/register?ticketType=0dd86188-b195-4c30-9285-969d81a89e18" target="_blank">Register</a></li>
</ul>

<h2 id="game-developers">Game Developers</h2>

<h3 id="introduction-to-xna">Introduction to XNA</h3>

<h4 id="xna-game-basics">XNA Game Basics</h4>

<ol>
  <li>What is XNA</li>
  <li>Game Development Tools</li>
  <li>XNA Game Projects</li>
  <li>XNA Game Loop</li>
  <li>Debugging Games</li>
</ol>

<h4 id="working-with-images-sounds-and-text">Working with Images, Sounds, and Text</h4>

<ol>
  <li>Working with Textures</li>
  <li>Playing Songs and Sound Effects</li>
  <li>Drawing text with SpriteFonts</li>
</ol>

<h4 id="getting-user-input">Getting User Input</h4>

<ol>
  <li>Getting input on the phone</li>
  <li>Using the Accelerometer</li>
  <li>Using Touch</li>
</ol>

<h3 id="game-state-management">Game State Management</h3>

<h4 id="managing-game-play">Managing Game Play</h4>

<ol>
  <li>Keeping Score</li>
  <li>Tracking Health and Lives</li>
  <li>Adding Levels</li>
</ol>

<h4 id="managing-screens">Managing Screens</h4>

<ol>
  <li>Creating Multi-Screen games</li>
  <li>Loading content in the background</li>
  <li>How to pause the game</li>
</ol>

<h4 id="managing-state">Managing State</h4>

<ol>
  <li>Phone Application Lifecycle</li>
  <li>Supporting Fast Application Switching</li>
  <li>Persisting and Restoring State</li>
</ol>

<h4 id="silverlight-and-xna-integration">Silverlight and XNA Integration</h4>

<ol>
  <li>Introduction to Silverlight</li>
  <li>Silverlight and XNA Integration</li>
  <li>Creating a Silverlight and XNA Game</li>
</ol>

<h3 id="advanced-xna-games">Advanced XNA Games</h3>

<h4 id="marketplace-and-advertisements">Marketplace and Advertisements</h4>

<ol>
  <li>Understanding the Windows Phone Marketplace</li>
  <li>Submitting your Game to the Marketplace</li>
  <li>Adding Advertisements to your game</li>
</ol>

<h4 id="3d-games">3D Games</h4>

<ol>
  <li>3D Support in XNA</li>
  <li>Creating a simple 3D Game</li>
</ol>

<h4 id="multi-platform-games">Multi-Platform Games</h4>

<ol>
  <li>Building Games for Phone, PC, and XBOX</li>
  <li>Other Multi-Platform options</li>
</ol>

<h4 id="cloud-integration">Cloud Integration</h4>

<ol>
  <li>Using Windows Azure</li>
  <li>Social Gaming Toolkit</li>
</ol>

<h2 id="business-applications-developers">Business Applications Developers</h2>

<h3 id="introduction-to-windows-phone-7-programming">Introduction to Windows Phone 7 Programming</h3>

<p>In this session, we start with a discussion of windows phone, the architecture changes made from 6.5 to 7.0, the hardware specifications and then move into the beginnings of building a WP7 application including…</p>

<ol>
  <li>Application life cycle</li>
  <li>Hardware Foundation</li>
  <li>Files associated with the project template</li>
  <li>Splash screen and the importance of 1 second /19-second loading</li>
  <li>Application Bar</li>
  <li>Panorama and Pivot controls</li>
  <li>MVVM</li>
  <li>Marketplace</li>
</ol>

<h3 id="connecting-to-services">Connecting to Services</h3>

<p>In this session, we will discuss how Cloud Services help to bring power to the phone. We will be binding to a rest based services and show how to search and display the information received. In this session we will also talk about Navigation, passing information between screens, while working with List and detail information.</p>

<ol>
  <li>Navigation</li>
  <li>Location</li>
  <li>JSON Deserialization</li>
  <li>Bing Maps</li>
  <li>Isolated Storage</li>
  <li>Binding Sample Data</li>
  <li>Navigation</li>
</ol>

<h4 id="recording-data">Recording Data</h4>

<p>In this session, we will be adding to our knowledge and learn the importance of live tiles. We will show you how to set up a periodic agent and how to set up and read and write to a SQL Database on Windows Phone.</p>

<ol>
  <li>Live Tiles</li>
  <li>SQL CE</li>
  <li>Background Processes and Periodic Agents</li>
  <li>Launchers and Choosers</li>
</ol>]]></content><author><name>Joseph Guadagno</name></author><category term="Technology News" /><category term="Events" /><category term="Community" /><category term="Arizona" /><summary type="html"><![CDATA[There are two events Windows Phone Unleashed events coming to the Phoenix metropolitan area in May. Please register soon before the events sell out. Registration and prerequisites can be found next to each of the events.]]></summary></entry><entry><title type="html">Motivation</title><link href="https://www.josephguadagno.net/2012/04/09/motivation/" rel="alternate" type="text/html" title="Motivation" /><published>2012-04-09T08:48:00-07:00</published><updated>2012-04-09T08:48:00-07:00</updated><id>https://www.josephguadagno.net/2012/04/09/motivation</id><content type="html" xml:base="https://www.josephguadagno.net/2012/04/09/motivation/"><![CDATA[<p>According to the <a href="https://www.bing.com/Dictionary/" target="_blank">Bing Dictionary</a>, <a href="https://www.bing.com/Dictionary/search?q=define+motivation&amp;qpvt=definition+motivation&amp;FORM=DTPDIA" target="_blank">motivation</a> is defined as follows:</p>

<ol>
  <li><strong>giving of reason to act:</strong> the act of giving somebody a reason or incentive to do something</li>
  <li><strong>enthusiasm:</strong> a feeling of enthusiasm, interest, or commitment that makes somebody want to do something, or something that causes such a feeling</li>
  <li><strong>reason:</strong> a reason for doing something or behaving in a particular way</li>
</ol>

<p>Motivation can be a powerful driver to move you forward in your life, goals and/or projects.  For the last few months I have been haphazardly trying to lose some weight, and by haphazardly I mean, that the intent was there just not a lot of motivation.</p>

<p>That changed two weeks ago at DevConnections when I had two friends of mine comment on my clothing and overall appearance.  One said to my wife, admittedly jokingly, <em>How can you go out with this guy looking like this when you are so beautiful</em> (paraphrasing a bit)? And another making comment while I was working the INETA booth commenting on my attire and general appearance stating (paraphrasing again), <em>How can you represent INETA looking like that?</em> Based on the other comments from that person made thought out the day all I heard was <em>You look like a fat slob!</em> And after looking at myself and hearing what some friends were saying to me, I realized that since my mother passed back in October of 09, and taking on the extra community work that I have let myself go.  I tended to wear baggier clothes that were comfortable and I wore sneakers everywhere which was not typical of my style.</p>

<p>Well DevConnections and some friends like <a href="https://www.elijahmanor.com/" target="_blank">Elijah Manor</a>, <a href="https://www.elijahmanor.com/2012/04/how-programmer-lost-46-pounds-and.html" target="_blank">loosing 46 pounds</a>, I am now <strong>motivated</strong> again. I am, as of this morning, 238 pounds and according to most charts and sites based on my height, I should be around 165 pounds although I would be happy with around 180 pounds. So that means I need to lose 58 pounds which I am capable of doing.  In a previous lifestyle change, I lost around 75 pounds.</p>

<p>There are many ways that one can do this; surgery, really restricting my diet, going through crash diets or detox diets. I am probably going to go back to watching what I eat and drink with Weight Watchers. I was really successful with this plan in the past.</p>

<h2 id="motivation-for-you">Motivation for You!</h2>

<p>Are you looking for that push to get you to start losing weight well join me for the first leg of my weight loss journey? The <a href="https://twitter.com/#!/search/%23TechEd20" target="_blank">#TechEd20</a>! That’s right, I set my first goal to lose 20 pounds by <a href="https://www.microsoft.com/click/services/Redirect2.ashx?CR_CC=200081762" target="_blank">TechEd North America</a> and decided to make a little competition of it.  Join me in <del>my</del> our weight loss quest for some cools prizes. What the prizes are I do not know yet, but I am sure I will be able to get some books or free software for participants.  But the biggest prize will be being able to fit into the jeans or your favorite shirt again.</p>

<p>So if you want to participate, leave a comment on this post and email me your target weight loss delta in pounds.  If you hit at least 20 pounds by the start of TechEd (Jun 10th), you get some sort of prize.  Everyone will most likely get something for participating.</p>

<p>Good luck!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Personal" /><summary type="html"><![CDATA[According to the Bing Dictionary, motivation is defined as follows:]]></summary></entry><entry><title type="html">Foursquare Autocomplete jQuery Plugin</title><link href="https://www.josephguadagno.net/2012/03/19/foursquare-autocomplete-jquery-plugin/" rel="alternate" type="text/html" title="Foursquare Autocomplete jQuery Plugin" /><published>2012-03-19T11:09:00-07:00</published><updated>2012-03-19T11:09:00-07:00</updated><id>https://www.josephguadagno.net/2012/03/19/foursquare-autocomplete-jquery-plugin</id><content type="html" xml:base="https://www.josephguadagno.net/2012/03/19/foursquare-autocomplete-jquery-plugin/"><![CDATA[<p><em>This post will go over implementing the foursquare Autocomplete jQuery plugin. In the upcoming days (weeks) I will have a few blog posts on using the foursquare API with C#.</em> A few months ago I was making updates for the <a href="https://www.mvpsummitevents.info" title="MVP Summit Events" target="_blank">MVP Summit Events</a> site, one of the features I wanted to add was <a href="https://www.foursquare.com" target="_blank">foursquare</a> integration to the site. I was thinking that it would be cool for each of the parties listed on the site, to show how many people have checked in to that event (venue) on foursquare. This way you could see what parties to attend and which ones to avoid :smile:. In order to determine who was checked in at one of the events, I needed to add the foursquare venue id to all of my venues. For the existing venues, I manually added the foursquare venue id but for new venues I was thinking of making the user experience as easy as possible, a user should enter a few characters and get venues in the area with those characters. The first thing that popped into my head was to use an auto complete control and use foursquare as the data source. So after “Googling it with Bing” I found that there was one control that did this but no longer worked. So what does every good developer do, re-invent the wheel :smile:.</p>

<h2 id="getting-started-with-the-foursquare-api">Getting Started with the foursquare API</h2>

<p>Just like most social media sites now, foursquare has a REST based API. Foursquare provides a <a href="https://developer.foursquare.com/overview/" target="_blank">quick start</a> guide to get you started using their API. In order to get started with the foursquare API you will need the following:</p>

<ul>
  <li>A foursquare account (<em>The site does recommend you create a separate foursquare account for your applications.</em>)</li>
  <li>An <a href="https://developer.foursquare.com/overview/auth" target="_blank">access token</a> for the API</li>
  <li><a href="https://foursquare.com/oauth" target="_blank">Register your application</a>.</li>
</ul>

<p>foursquare does have many API <a href="https://developer.foursquare.com/docs/" target="_blank">end points</a> for accessing their data. After checking out several of them I decided to go with the <a href="https://developer.foursquare.com/docs/venues/suggestcompletion" target="_blank">Suggest Completion Venues</a> end point even though (at the time of this writing it was an experimental feature).</p>

<h3 id="suggest-completion-end-point">Suggest Completion End Point</h3>

<p>According to the API end point <a href="https://developer.foursquare.com/docs/venues/suggestcompletion" target="_blank">documentation</a> for the Suggest Completion end point, the end point provides a list of mini-venues partially matching the search term, near the location. The method uses the HTTP GET verb and has two required parameters; <strong>ll</strong> and <strong>query</strong>. The <strong>ll</strong> parameter which is the latitude and longitude of the users location (or search area) . And the <strong>query</strong> parameter is the string you want to search for. The results that get returned is an array of mini venues (essentially an arrays without a lot of extra properties, just the minimum set required for a suggestion. This was perfect for my needs.</p>

<h2 id="creating-the-plugin">Creating the plugin</h2>

<p>I decided to go with using the <a href="https://jqueryui.com/demos/autocomplete/" target="_blank">auto complete</a> widget from the <a href="https://jqueryui.com/" target="_blank">jQuery UI</a>, use the suggest completion end point from foursquare and turn it into a jQuery plugin. Unfortunately I have never created a jQuery plugin before, however, I was lucky enough to run across a post from a buddy of mine <a href="https://elijahmanor.com/" target="_blank">Elijah Manor</a> on <a href="https://msdn.microsoft.com/en-us/scriptjunkie/ff608209?WT.mc_id=DT-MVP-4024623" target="_blank">How to Create Your Own jQuery Plugin</a>. After a few hours of fiddling around with the cost from the blog post above I eventually figured it out and got it to work. The resulting control looks like this:</p>

<p><a href="/assets/images/posts/image_8.png"><img src="/assets/images/posts/image_thumb_7.png" alt="image" title="image" /></a></p>

<p>In order to use the foursquare jQuery auto complete plugin you will need to include on your page, <a href="https://docs.jquery.com/Downloading_jQuery" target="_blank">jQuery</a>, <a href="https://jqueryui.com/download" target="_blank">jQuery UI</a>, one of the jQuery UI <a href="https://jqueryui.com/themeroller/" target="_blank">themes</a> and this plugin, <a href="/assets/downloads/4sqacplugin.js">4sqacplugin</a></p>

<p>I’ve also included a sample page to get you started. It has a bunch of styles to make the use of the plugin a little cleaner. <a href="/assets/downloads/4sqautocomplete.html">4sqautocomplete</a> Once you have the required files referenced on your page, you can “foursquare auto complete” a textbox by calling the <code class="language-plaintext highlighter-rouge">foursquareAutocomplete</code> method as shown here:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="rouge-code"><pre><span class="nx">$</span><span class="p">(</span><span class="dl">"</span><span class="s2">#venue</span><span class="dl">"</span><span class="p">).</span><span class="nx">foursquareAutocomplete</span><span class="p">({</span>
  <span class="dl">'</span><span class="s1">latitude</span><span class="dl">'</span><span class="p">:</span> <span class="mf">47.22</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">longitude</span><span class="dl">'</span><span class="p">:</span> <span class="o">-</span><span class="mf">122.2</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">oauth_token</span><span class="dl">'</span><span class="p">:</span> <span class="dl">"</span><span class="s2">your oauth token</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">minLength</span><span class="dl">'</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">search</span><span class="dl">'</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">ui</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#venue-name</span><span class="dl">'</span><span class="p">).</span><span class="nx">html</span><span class="p">(</span><span class="nx">ui</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
    <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#venue-id</span><span class="dl">'</span><span class="p">).</span><span class="nx">val</span><span class="p">(</span><span class="nx">ui</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">id</span><span class="p">);</span>
    <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#venue-address</span><span class="dl">'</span><span class="p">).</span><span class="nx">html</span><span class="p">(</span><span class="nx">ui</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">address</span><span class="p">);</span>
    <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#venue-cityLine</span><span class="dl">'</span><span class="p">).</span><span class="nx">html</span><span class="p">(</span><span class="nx">ui</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">cityLine</span><span class="p">);</span>
    <span class="nx">$</span><span class="p">(</span><span class="dl">'</span><span class="s1">#venue-icon</span><span class="dl">'</span><span class="p">).</span><span class="nx">attr</span><span class="p">(</span><span class="dl">"</span><span class="s2">src</span><span class="dl">"</span><span class="p">,</span> <span class="nx">ui</span><span class="p">.</span><span class="nx">item</span><span class="p">.</span><span class="nx">photo</span><span class="p">);</span>
    <span class="k">return</span> <span class="kc">false</span><span class="p">;</span>
  <span class="p">},</span>
  <span class="dl">'</span><span class="s1">onError</span><span class="dl">'</span> <span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">errorCode</span><span class="p">,</span> <span class="nx">errorType</span><span class="p">,</span> <span class="nx">errorDetail</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">message</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Foursquare Error: Code=</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">errorCode</span> <span class="o">+</span>
    <span class="dl">"</span><span class="s2">, errorType= </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">errorType</span> <span class="o">+</span>
    <span class="dl">"</span><span class="s2">, errorDetail= </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">errorDetail</span><span class="p">;</span>
    <span class="nx">log</span><span class="p">(</span><span class="nx">message</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">});</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>latitude</td>
      <td>The latitude where you want to look for the venue.</td>
    </tr>
    <tr>
      <td>longitude</td>
      <td>The longitude where you want to look for the venue.</td>
    </tr>
    <tr>
      <td>oauth_token</td>
      <td>Your foursquare oauth token.</td>
    </tr>
    <tr>
      <td>minLength</td>
      <td>The minimum length the text should be before you kick off the search.</td>
    </tr>
    <tr>
      <td>search</td>
      <td>This event is fired once the user selects a venue from the list.</td>
    </tr>
    <tr>
      <td>onError</td>
      <td>This event is fired if there is an error returned from the foursquare REST API.</td>
    </tr>
    <tr>
      <td>onAjaxError</td>
      <td>This event is fired if there is an error making the call to the foursquare REST API.</td>
    </tr>
  </tbody>
</table>

<h3 id="handling-the-results">Handling the results</h3>

<p>Once a user selects the venue from the list, the search event is raised. The search event has two parameters:</p>

<ul>
  <li>Event: The event</li>
  <li>Item: the foursquare venue that was returned.</li>
</ul>

<p>The item that is returned is a custom object that provides the basic address properties for the venue. The properties combined, will look like a US formatted address and will take into account fields that are not populated in foursquare.</p>

<p><a href="/assets/images/posts/image_9.png"><img src="/assets/images/posts/image_thumb_8.png" alt="image" title="image" /></a></p>

<table>
  <thead>
    <tr>
      <th>Property Name</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>name</td>
      <td>The name of the venue</td>
    </tr>
    <tr>
      <td>id</td>
      <td>The foursquare venue id</td>
    </tr>
    <tr>
      <td>address</td>
      <td>The address line (formatted like a US address) address1, address2</td>
    </tr>
    <tr>
      <td>cityLine</td>
      <td>The city line (formatted like a US address). Chandler, AZ 85286</td>
    </tr>
    <tr>
      <td>photo</td>
      <td>The 32-pixel image that foursquare uses for the venue.</td>
    </tr>
    <tr>
      <td>full</td>
      <td>The entire <a href="https://developer.foursquare.com/docs/venues/suggestcompletion" target="_blank">mini-venue</a> response returned from foursquare.</td>
    </tr>
  </tbody>
</table>

<h2 id="conclusion">Conclusion</h2>

<p>It took a while for me to figure out all of the parts needed to create the jQuery plugin but overall I think it was worth it. Again this is my first venture into creating a jQuery plugin, so if it is way off, let me know.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Foursquare" /><category term="jQuery" /><category term="jQuery Plugin" /><category term="Plugin" /><category term="Web" /><summary type="html"><![CDATA[This post will go over implementing the foursquare Autocomplete jQuery plugin. In the upcoming days (weeks) I will have a few blog posts on using the foursquare API with C#. A few months ago I was making updates for the MVP Summit Events site, one of the features I wanted to add was foursquare integration to the site. I was thinking that it would be cool for each of the parties listed on the site, to show how many people have checked in to that event (venue) on foursquare. This way you could see what parties to attend and which ones to avoid :smile:. In order to determine who was checked in at one of the events, I needed to add the foursquare venue id to all of my venues. For the existing venues, I manually added the foursquare venue id but for new venues I was thinking of making the user experience as easy as possible, a user should enter a few characters and get venues in the area with those characters. The first thing that popped into my head was to use an auto complete control and use foursquare as the data source. So after “Googling it with Bing” I found that there was one control that did this but no longer worked. So what does every good developer do, re-invent the wheel :smile:.]]></summary></entry><entry><title type="html">Add Google Plus One Extension to BlogEngine.NET</title><link href="https://www.josephguadagno.net/2012/01/18/add-google-plus-one-extension-to-blogengine-net/" rel="alternate" type="text/html" title="Add Google Plus One Extension to BlogEngine.NET" /><published>2012-01-18T10:45:00-07:00</published><updated>2012-01-18T10:45:00-07:00</updated><id>https://www.josephguadagno.net/2012/01/18/add-google-plus-one-extension-to-blogengine-net</id><content type="html" xml:base="https://www.josephguadagno.net/2012/01/18/add-google-plus-one-extension-to-blogengine-net/"><![CDATA[<p>A few years ago I built my personal website using <a href="https://www.dotnetblogengine.net/" target="_blank">BlogEngine.NET</a>, every once in a while I tend to pay attention to the site and “freshen it up”.  Back in December of 2011, I upgraded to version 2.5 of BlogEngine.NET and updated the theme.  Last night I added <a href="https://facebook.com" target="_blank">facebook</a> <a href="https://developers.facebook.com/docs/reference/plugins/like/" target="_blank">like</a> buttons to all those post and pages courtesy of <a href="https://isharpnote.com/isharpnote/post/2011/03/17/Facebook-Like-Button-Extension-For-BlogEngine-20.aspx" title="Facebook Like Button Extension For BlogEngine 2.0" target="_blank">isharpnote</a>. Well I wanted to add a <a href="https://www.google.com/+1/button/">Google Plus One</a> {:target=”_blank”}button to my site also but I could not find any BlogEngine.NET extensions for it, so what does every developer do, create one.  I started with the “<a href="https://isharpnote.com/isharpnote/post/2011/07/24/Facebook-Like-with-Google-Plus-Extension-for-BlogEngine-25.aspx" target="_blank">Facebook Like with Google Plus Extension for BlogEngine 2.5</a>” extension from isharpnote and tweaked it for my needs.</p>

<h2 id="creating-the-extension">Creating the extension</h2>

<p>To create a BlogEngine.NET extension you need a class and attribute it with the BlogEngine.Core.Web.Extensions.Extension attribute, like so:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="nf">Extension</span><span class="p">(</span>
<span class="s">"Adds the Google Plus One to your blog Posts"</span><span class="p">,</span>
<span class="s">"1.0"</span><span class="p">,</span>
<span class="s">"&lt;a href='https://www.josephguadagno.net'&gt;Joseph Guadagno&lt;/a&gt;"</span><span class="p">,</span>
<span class="m">800</span><span class="p">)]</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Parameters explained</p>

<table>
  <thead>
    <tr>
      <th>Parameter Name</th>
      <th>Used for</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Description</td>
      <td>A description of your extension</td>
    </tr>
    <tr>
      <td>Version</td>
      <td>The version of your extension</td>
    </tr>
    <tr>
      <td>Author</td>
      <td>A link to the author of the extension</td>
    </tr>
    <tr>
      <td>Priority</td>
      <td>The priority of the extension, in relation to others.</td>
    </tr>
  </tbody>
</table>

<p>This class will need to reside in the <code class="language-plaintext highlighter-rouge">App_Code\Extensions</code> folder of your site. Next step, if you want to have settings, you will need to tell the extension manager that you have settings.  You do this by executing code similar to this, in the constructor of your extension or a method that is called in the constructor of your extension.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="kt">var</span> <span class="n">settings</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">ExtensionSettings</span><span class="p">(</span><span class="n">ExtensionName</span><span class="p">)</span>
  <span class="p">{</span><span class="n">Help</span> <span class="p">=</span> <span class="s">"Adds Google Plus One to your Post Home page and Post page"</span><span class="p">,</span> <span class="n">IsScalar</span> <span class="p">=</span> <span class="k">true</span><span class="p">};</span>
  <span class="n">settings</span><span class="p">.</span><span class="nf">AddParameter</span><span class="p">(</span><span class="s">"size"</span><span class="p">,</span> <span class="s">"Size"</span><span class="p">,</span> <span class="m">20</span><span class="p">,</span> <span class="k">false</span><span class="p">,</span> <span class="k">false</span><span class="p">,</span> <span class="n">ParameterType</span><span class="p">.</span><span class="n">ListBox</span><span class="p">);</span>
  <span class="n">settings</span><span class="p">.</span><span class="nf">AddValue</span><span class="p">(</span><span class="s">"size"</span><span class="p">,</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="s">"standard"</span><span class="p">,</span> <span class="s">"small"</span><span class="p">,</span> <span class="s">"medium"</span><span class="p">,</span> <span class="s">"tall"</span> <span class="p">},</span> <span class="err">'</span><span class="n">standard</span><span class="err">'</span><span class="p">);</span>
  <span class="n">ExtensionSettings</span> <span class="p">=</span> <span class="n">ExtensionManager</span><span class="p">.</span><span class="nf">InitSettings</span><span class="p">(</span><span class="n">ExtensionName</span><span class="p">,</span> <span class="n">settings</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In my extension I call a method called InitializeSettings in the constructor</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="nf">GooglePlusOne</span><span class="p">()</span>
<span class="p">{</span>
  <span class="nf">InitializeSettings</span><span class="p">();</span>
  <span class="n">Post</span><span class="p">.</span><span class="n">Serving</span> <span class="p">+=</span> <span class="n">ServingHandler</span><span class="p">;</span>
  <span class="n">Page</span><span class="p">.</span><span class="n">Serving</span> <span class="p">+=</span> <span class="n">ServingHandler</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>You’ll notice that I attached two events, Post.Serving and Page.Serving, to the ServingHandler method. This will tell BlogEngine.NET that I want to know when it is serving up pages or blog posts.</p>

<h3 id="handling-the-page-and-post-serving">Handling the page and post serving.</h3>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="rouge-code"><pre><span class="k">void</span> <span class="nf">ServingHandler</span><span class="p">(</span><span class="kt">object</span> <span class="n">sender</span><span class="p">,</span> <span class="n">ServingEventArgs</span> <span class="n">e</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">if</span> <span class="p">(!</span><span class="n">ExtensionManager</span><span class="p">.</span><span class="nf">ExtensionEnabled</span><span class="p">(</span><span class="n">ExtensionName</span><span class="p">))</span> <span class="k">return</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">Location</span> <span class="p">==</span> <span class="n">ServingLocation</span><span class="p">.</span><span class="n">Feed</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>

  <span class="n">HttpContext</span> <span class="n">context</span> <span class="p">=</span> <span class="n">HttpContext</span><span class="p">.</span><span class="n">Current</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">context</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="n">System</span><span class="p">.</span><span class="n">Web</span><span class="p">.</span><span class="n">UI</span><span class="p">.</span><span class="n">Page</span> <span class="n">page</span> <span class="p">=</span> <span class="n">context</span><span class="p">.</span><span class="n">CurrentHandler</span> <span class="k">as</span> <span class="n">System</span><span class="p">.</span><span class="n">Web</span><span class="p">.</span><span class="n">UI</span><span class="p">.</span><span class="n">Page</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">page</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="nf">ScriptInject</span><span class="p">(</span><span class="n">page</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="kt">var</span> <span class="n">post</span> <span class="p">=</span> <span class="n">sender</span> <span class="k">as</span> <span class="n">Post</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">post</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
      <span class="n">e</span><span class="p">.</span><span class="n">Body</span> <span class="p">+=</span> <span class="nf">GetButton</span><span class="p">(</span><span class="n">post</span><span class="p">.</span><span class="n">AbsoluteLink</span><span class="p">.</span><span class="n">AbsoluteUri</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>On line 3 you’ll not the check to see if the extension is enabled.  Believe it or not, people might turn off your extension. Line 4 checks to see if this is in a feed, if so, we do not want to apply the button to the page or post. Line 6 - 10, I get a reference to the page so that I can inject the required JavaScript for the Google Plus One button.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="nf">ScriptInject</span><span class="p">(</span><span class="n">System</span><span class="p">.</span><span class="n">Web</span><span class="p">.</span><span class="n">UI</span><span class="p">.</span><span class="n">Page</span> <span class="n">page</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">HtmlGenericControl</span> <span class="n">googleScript</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">HtmlGenericControl</span><span class="p">(</span><span class="s">"script"</span><span class="p">);</span>
  <span class="n">googleScript</span><span class="p">.</span><span class="n">Attributes</span><span class="p">[</span><span class="s">"type"</span><span class="p">]</span> <span class="p">=</span> <span class="s">"text/javascript"</span><span class="p">;</span>
  <span class="n">googleScript</span><span class="p">.</span><span class="n">Attributes</span><span class="p">[</span><span class="s">"src"</span><span class="p">]</span> <span class="p">=</span> <span class="s">"https://apis.google.com/js/plusone.js"</span><span class="p">;</span>
  <span class="n">page</span><span class="p">.</span><span class="n">Header</span><span class="p">.</span><span class="n">Controls</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">googleScript</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Next, I get a reference to the post and append to the body of the post the Google Plus One button.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">static</span> <span class="kt">string</span> <span class="nf">GetButton</span><span class="p">(</span><span class="kt">string</span> <span class="n">url</span><span class="p">)</span>
<span class="p">{</span>
  <span class="kt">string</span> <span class="n">size</span> <span class="p">=</span> <span class="n">ExtensionSettings</span><span class="p">.</span><span class="nf">GetSingleValue</span><span class="p">(</span><span class="s">"size"</span><span class="p">)</span> <span class="p">=</span> <span class="s">"standard"</span><span class="p">;</span>
  <span class="kt">string</span> <span class="n">googleButton</span> <span class="p">=</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="s">"&lt;g:plusone size='{0}' href='{1}'&gt;&lt;/g:plusone&gt;"</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">url</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">googleButton</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>That’s it. You can download the complete extension here <a href="/assets/downloads/GooglePlusOne.cs_.zip">GooglePlusOne.cs</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Web" /><summary type="html"><![CDATA[A few years ago I built my personal website using BlogEngine.NET, every once in a while I tend to pay attention to the site and “freshen it up”.  Back in December of 2011, I upgraded to version 2.5 of BlogEngine.NET and updated the theme.  Last night I added facebook like buttons to all those post and pages courtesy of isharpnote. Well I wanted to add a Google Plus One {:target=”_blank”}button to my site also but I could not find any BlogEngine.NET extensions for it, so what does every developer do, create one.  I started with the “Facebook Like with Google Plus Extension for BlogEngine 2.5” extension from isharpnote and tweaked it for my needs.]]></summary></entry><entry><title type="html">DebuggerDisplay Attribute</title><link href="https://www.josephguadagno.net/2012/01/12/debuggerdisplay-attribute/" rel="alternate" type="text/html" title="DebuggerDisplay Attribute" /><published>2012-01-12T10:49:00-07:00</published><updated>2012-01-12T10:49:00-07:00</updated><id>https://www.josephguadagno.net/2012/01/12/debuggerdisplay-attribute</id><content type="html" xml:base="https://www.josephguadagno.net/2012/01/12/debuggerdisplay-attribute/"><![CDATA[<p>Have you ever tried to debug an application and wish the Visual Studio debugger did not display <code class="language-plaintext highlighter-rouge">{Namespace.ObjectName}</code> when you wanted to see some of the details of the objects?</p>

<p><a href="/assets/images/posts/image_5.png"><img src="/assets/images/posts/image_thumb_4.png" alt="image" title="image" /></a></p>

<p>Visual Studio has an attribute that you can add to a class to inform the debugger what to display when it is displaying that class in the debugger. As you probably guessed the attribute is called <a href="https://msdn.microsoft.com/en-us/library/system.diagnostics.debuggerdisplayattribute.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">DebuggerDisplay</a>.</p>

<h2 id="how-to-implement">How to Implement</h2>

<p>Let’s say we have a simple class called <code class="language-plaintext highlighter-rouge">Person</code>, the Person class has 4 properties; <code class="language-plaintext highlighter-rouge">FirstName</code>, <code class="language-plaintext highlighter-rouge">MiddleName</code>, <code class="language-plaintext highlighter-rouge">LastName</code>, and <code class="language-plaintext highlighter-rouge">FullName</code>. Here is the definition:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">Person</span>
<span class="p">{</span>
  <span class="k">public</span> <span class="kt">string</span> <span class="n">FirstName</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>
  <span class="k">public</span> <span class="kt">string</span> <span class="n">MiddleName</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>
  <span class="k">public</span> <span class="kt">string</span> <span class="n">LastName</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>
  <span class="k">public</span> <span class="kt">string</span> <span class="n">FullName</span>
  <span class="p">{</span>
    <span class="k">get</span>
    <span class="p">{</span>
      <span class="k">return</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="s">"{0} {1}{2}"</span><span class="p">,</span>
        <span class="n">FirstName</span><span class="p">,</span>
        <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">MiddleName</span><span class="p">))</span> <span class="p">?</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span> <span class="p">:</span> <span class="n">MiddleName</span> <span class="p">+</span> <span class="s">" "</span><span class="p">,</span>
        <span class="n">LastName</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Next, let’s assume we want to display the first and last name of the person when debugging. We first need to add the DebuggerDisplay attribute to our class. The DebuggerDisplay attribute can be found in the <a href="https://msdn.microsoft.com/en-us/library/15t15zda.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">System.Diagnosis</a> class of the .NET framework. The DebuggerDisplay works almost like the string.Format method, except you, replace the numbers with the property/method names you want to display. Example:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="nf">DebuggerDisplay</span><span class="p">(</span><span class="s">"FirstName={FirstName} LastName={LastName}"</span><span class="p">)]</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This will tell the debugger to display the string <em>FirstName=</em> with the value of the <code class="language-plaintext highlighter-rouge">FirstName</code> field in double quotes followed by <code class="language-plaintext highlighter-rouge">LastName=</code> with the value of the <code class="language-plaintext highlighter-rouge">LastName</code> field in double-quotes every time it needs to display a person object. Here is our new class:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre><span class="p">[</span><span class="nf">DebuggerDisplay</span><span class="p">(</span><span class="s">"FirstName={FirstName} LastName={LastName}"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Person</span>
<span class="p">{</span>
  <span class="k">public</span> <span class="kt">string</span> <span class="n">FirstName</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>
  <span class="k">public</span> <span class="kt">string</span> <span class="n">MiddleName</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>
  <span class="k">public</span> <span class="kt">string</span> <span class="n">LastName</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>
  <span class="k">public</span> <span class="kt">string</span> <span class="n">FullName</span>
  <span class="p">{</span>
    <span class="k">get</span>
    <span class="p">{</span>
      <span class="k">return</span> <span class="kt">string</span><span class="p">.</span><span class="nf">Format</span><span class="p">(</span><span class="s">"{0} {1}{2"</span><span class="p">,</span> <span class="n">FirstName</span><span class="p">,</span>
          <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">MiddleName</span><span class="p">))</span> <span class="p">?</span> <span class="kt">string</span><span class="p">.</span><span class="n">Empty</span> <span class="p">:</span> <span class="n">MiddleName</span> <span class="p">+</span> <span class="s">" "</span><span class="p">,</span>
          <span class="n">LastName</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This will display like so:</p>

<p><a href="/assets/images/posts/image_6.png"><img src="/assets/images/posts/image_thumb_5.png" alt="image" title="image" /></a></p>

<p>You’ll notice this makes it easier to see what you are looking at. It also works in the immediate window.</p>

<p><a href="/assets/images/posts/image_7.png"><img src="/assets/images/posts/image_thumb_6.png" alt="image" title="image" /></a></p>

<h2 id="summary">Summary</h2>

<p>You can use more than just field names. Method calls can be done (although probably not the best) and some calculations. Take a look at the MSDN documentation for the <a href="https://msdn.microsoft.com/en-us/library/system.diagnostics.debuggerdisplayattribute.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">DebuggerDisplay</a> attribute for more information. There is also an article titled <a href="https://blogs.msdn.com/b/jaredpar/archive/2011/03/18/debuggerdisplay-attribute-best-practices.aspx" target="_blank">DebuggerDisplay attribute best practices</a> that you should read also.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Visual Studio" /><category term=".NET" /><summary type="html"><![CDATA[Have you ever tried to debug an application and wish the Visual Studio debugger did not display {Namespace.ObjectName} when you wanted to see some of the details of the objects?]]></summary></entry><entry><title type="html">Using an ASP.NET DataRepeater ItemTemplate to create a jQuery Mobile Nested List with List Dividers</title><link href="https://www.josephguadagno.net/2012/01/01/using-an-asp-net-datarepeater-itemtemplate-to-create-a-jquery-mobile-nested-list-with-list-dividers/" rel="alternate" type="text/html" title="Using an ASP.NET DataRepeater ItemTemplate to create a jQuery Mobile Nested List with List Dividers" /><published>2011-12-31T17:45:00-07:00</published><updated>2011-12-31T17:45:00-07:00</updated><id>https://www.josephguadagno.net/2012/01/01/using-an-asp-net-datarepeater-itemtemplate-to-create-a-jquery-mobile-nested-list-with-list-dividers</id><content type="html" xml:base="https://www.josephguadagno.net/2012/01/01/using-an-asp-net-datarepeater-itemtemplate-to-create-a-jquery-mobile-nested-list-with-list-dividers/"><![CDATA[<p>The jQuery Mobile framework has a <a href="https://jquerymobile.com/demos/1.0/docs/lists/docs-lists.html" target="_blank">list view widget</a> that displays unordered lists in several different ways. In the process of converting the <a href="https://mvpsummitevents.info/m/" target="_blank">Microsoft Global MVP Summit mobile</a> site to use jQuery Mobile, I wanted to change the way I displayed the events. The idea was to have a header for each date that there was an event along with some of the details of the event. Clicking on the event would take you the to event details page. To accomplish this I used the <a href="https://jquerymobile.com/demos/1.0/docs/lists/lists-count.html" target="_blank">count bubble</a>, <a href="https://jquerymobile.com/demos/1.0/docs/lists/lists-divider.html" target="_blank">list dividers</a>, <a href="https://jquerymobile.com/demos/1.0/docs/lists/lists-formatting.html" target="_blank">content formatting</a> and the <a href="https://jquerymobile.com/demos/1.0/docs/lists/lists-search.html" target="_blank">search filter bar</a> features of jQuery Mobile. First let’s look at the HTML, for the sake of brevity I removed the ID fields from the HTML.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;ul</span> <span class="na">data-role=</span><span class="s">"listview"</span> <span class="na">data-inset=</span><span class="s">"true"</span> <span class="na">data-theme=</span><span class="s">"d"</span> <span class="na">data-filter=</span><span class="s">"true"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;li</span> <span class="na">data-role=</span><span class="s">"list-divider"</span><span class="nt">&gt;</span>2/25/2012<span class="nt">&lt;span</span> <span class="na">id=</span><span class="s">"EventCount_0"</span> <span class="na">class=</span><span class="s">"ui-li-count"</span><span class="nt">&gt;</span>1<span class="nt">&lt;/span&gt;&lt;/li&gt;</span>
  <span class="nt">&lt;li&gt;</span>
    <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"/m/e.aspx?Id=45"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;h3&gt;&lt;span&gt;</span>Northwest Harvest at MVP Summit 2012<span class="nt">&lt;/span&gt;&lt;/h3&gt;</span>
      <span class="nt">&lt;p&gt;&lt;strong&gt;&lt;span&gt;</span>Northwest Harvest Warehouse<span class="nt">&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;</span>
      <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">"ui-li-aside"</span><span class="nt">&gt;&lt;span&gt;</span>from 11:30 AM to 5:00 PM<span class="nt">&lt;/span&gt;&lt;/p&gt;</span>
    <span class="nt">&lt;/a&gt;</span>
  <span class="nt">&lt;/li&gt;</span>
  <span class="nt">&lt;li</span> <span class="na">data-role=</span><span class="s">"list-divider"</span><span class="nt">&gt;</span>2/27/2012<span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">"ui-li-count"</span><span class="nt">&gt;</span>2<span class="nt">&lt;/span&gt;&lt;/li&gt;</span>
  <span class="nt">&lt;li&gt;</span>
    <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"/m/e.aspx?Id=48"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;h3&gt;&lt;span&gt;</span>Consumer Camp: Bellevue<span class="nt">&lt;/span&gt;&lt;/h3&gt;</span>
      <span class="nt">&lt;p&gt;&lt;strong&gt;&lt;span</span> <span class="na">id=</span><span class="s">"VenueName_0"</span><span class="nt">&gt;</span>Microsoft Store: Bellevue<span class="nt">&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;</span>
      <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">"ui-li-aside"</span><span class="nt">&gt;&lt;span&gt;</span>from 5:00 PM to 8:00 PM<span class="nt">&lt;/span&gt;&lt;/p&gt;</span>
    <span class="nt">&lt;/a&gt;</span>
  <span class="nt">&lt;/li&gt;</span>
  <span class="nt">&lt;li&gt;</span>
    <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"/m/e.aspx?Id=46"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;h3&gt;&lt;span&gt;</span>First Time MVPs Event<span class="nt">&lt;/span&gt;&lt;/h3&gt;</span>
      <span class="nt">&lt;p&gt;&lt;strong&gt;&lt;span&gt;</span>Rockbottom – Top of the Rock<span class="nt">&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;</span>
      <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">"ui-li-aside"</span><span class="nt">&gt;&lt;span&gt;</span>from 6:00 PM to 9:00 PM<span class="nt">&lt;/span&gt;&lt;/p&gt;</span>
    <span class="nt">&lt;/a&gt;</span>
  <span class="nt">&lt;/li&gt;</span>
<span class="nt">&lt;/ul&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>From this HTML markup the jQuery Mobile framework will generate a view that looks like this: <a href="/assets/images/posts/image_3.png"><img src="/assets/images/posts/image_thumb_2.png" alt="image" title="image" /></a></p>

<h2 id="the-jquery-mobile-listview">The jQuery Mobile ListView</h2>

<h3 id="creating-the-listview">Creating the ListView</h3>

<p>The start of the unordered list, Line 1, has 4 attributes:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;ul</span> <span class="na">data-role=</span><span class="s">"listview"</span> <span class="na">data-inset=</span><span class="s">"true"</span> <span class="na">data-theme=</span><span class="s">"b"</span> <span class="na">data-filter=</span><span class="s">"true"</span><span class="nt">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">data-role</code> of <code class="language-plaintext highlighter-rouge">listview</code> tells the jQuery Mobile framework to use the jQuery Mobile <a href="https://jquerymobile.com/demos/1.0/docs/lists/docs-lists.html" target="_blank">Listview</a> widget. Setting the <code class="language-plaintext highlighter-rouge">data-inset</code> attribute to <code class="language-plaintext highlighter-rouge">true</code> tells the jQuery Mobile framework to indent the list view and add the rounded edges. The <code class="language-plaintext highlighter-rouge">data-theme</code> attribute tells jQuery Mobile to use the <code class="language-plaintext highlighter-rouge">d_theme</code>. Setting the <code class="language-plaintext highlighter-rouge">data-filter</code> equal to <code class="language-plaintext highlighter-rouge">true</code> tells the jQuery Mobile framework to add the filter items text box up top. No additional work is needed to add the filter, it will search all of the ListItems that are part of this unordered list who’s <code class="language-plaintext highlighter-rouge">data-role</code> attribute is not set to <code class="language-plaintext highlighter-rouge">list-divider</code> for the text entered.</p>

<h3 id="the-list-divider">The List Divider</h3>

<p>Adding the <code class="language-plaintext highlighter-rouge">data-role</code> of <code class="language-plaintext highlighter-rouge">list-divider</code> (Line 2 of the initial example) will make that list item appear as a divider. You can use this attribute to group items, in my case, I grouped by date. To establish the count bubble you will need to wrap the count of item around a span tag and give it the class of <code class="language-plaintext highlighter-rouge">ui-li-count</code>, as shown in Line 2. Here’s an annotated image with markup.</p>

<p><a href="/assets/images/posts/image_4.png"><img src="/assets/images/posts/image_thumb_3.png" alt="image" title="image" /></a></p>

<p>The item in red is the individual list item which will be discussed in the next section.</p>

<h3 id="the-items">The Items</h3>

<p>Each sub-item under the list divider needs to be it’s own Anchor element ( <code class="language-plaintext highlighter-rouge">A</code> ) wrapped in a ListItem (<code class="language-plaintext highlighter-rouge">LI</code>) tag as shown in lines 3-9 and 11-24 above.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;li&gt;</span>
  <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"/m/e.aspx?Id=45"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;h3&gt;&lt;span&gt;</span>Northwest Harvest at MVP Summit 2012<span class="nt">&lt;/span&gt;&lt;/h3&gt;</span>
    <span class="nt">&lt;p&gt;&lt;strong&gt;&lt;span&gt;</span>Northwest Harvest Warehouse<span class="nt">&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;</span>
    <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">"ui-li-aside"</span><span class="nt">&gt;&lt;span&gt;</span>from 11:30 AM to 5:00 PM<span class="nt">&lt;/span&gt;&lt;/p&gt;</span>
  <span class="nt">&lt;/a&gt;</span>
<span class="nt">&lt;/li&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Each <code class="language-plaintext highlighter-rouge">LI</code> formatted above will generate a “row” as highlighted in the red boxed image above within your browser. Clicking on one of those links will take you to the page specified in an Anchor tag. In order to get the times to “float” to the left, you will need to use the <code class="language-plaintext highlighter-rouge">ui-li-aside</code> class. Note, the &gt; image will get added automatically by the framework.</p>

<h2 id="creating-the-jquery-listview-with-an-aspnet-datarepeater-control">Creating the jQuery ListView with an ASP.NET DataRepeater Control</h2>

<p>In order to accomplish this, I went with a DataRepeater within a DataRepeater. The first, or outside, DataRepeater (<code class="language-plaintext highlighter-rouge">DateRepeater</code>)will get a list of distinct dates from the data store in order to create the List Dividers. The second, or inner, DataRepeater (<code class="language-plaintext highlighter-rouge">EventRepeater</code>) will list all of the events for the day specified by the <code class="language-plaintext highlighter-rouge">DateRepeater</code>. Let’s take a look at the code:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;asp:Repeater</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"DateRepeater"</span> <span class="na">DataSourceID=</span><span class="s">"EventDatesDataSource"</span> <span class="na">OnItemDataBound=</span><span class="s">"DateRepeaterOnItemDataBound"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;HeaderTemplate&gt;</span>
    <span class="nt">&lt;ul</span> <span class="na">data-role=</span><span class="s">"listview"</span> <span class="na">data-inset=</span><span class="s">"true"</span> <span class="na">data-theme=</span><span class="s">"d"</span> <span class="na">data-filter=</span><span class="s">"true"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;/HeaderTemplate&gt;</span>
  <span class="nt">&lt;ItemTemplate&gt;</span>
    <span class="nt">&lt;li</span> <span class="na">data-role=</span><span class="s">"list-divider"</span><span class="nt">&gt;</span><span class="err">&lt;</span>%# Container.DataItem %&gt;
      <span class="nt">&lt;asp:Label</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"EventCount"</span> <span class="na">class=</span><span class="s">"ui-li-count"</span><span class="nt">&gt;&lt;/asp:Label&gt;</span>
    <span class="nt">&lt;/li&gt;</span>
    <span class="nt">&lt;asp:Repeater</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"EventRepeater"</span> <span class="na">OnItemDataBound=</span><span class="s">"EventRepeaterOnItemDataBound"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;ItemTemplate&gt;</span>
        <span class="nt">&lt;li&gt;</span>
          <span class="nt">&lt;asp:HyperLink</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"EventUrl"</span><span class="nt">&gt;</span>
            <span class="nt">&lt;h3&gt;&lt;asp:Label</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"EventName"</span> <span class="nt">/&gt;&lt;/h3&gt;</span>
            <span class="nt">&lt;p&gt;&lt;strong&gt;&lt;asp:Label</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"VenueName"</span><span class="nt">&gt;&lt;/asp:Label&gt;&lt;/strong&gt;&lt;/p&gt;</span>
            <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">"ui-li-aside"</span><span class="nt">&gt;&lt;asp:Label</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"EventDate"</span><span class="nt">&gt;&lt;/asp:Label&gt;&lt;/p&gt;</span>
          <span class="nt">&lt;/asp:HyperLink&gt;</span>
        <span class="nt">&lt;/li&gt;</span>
      <span class="nt">&lt;/ItemTemplate&gt;</span>
    <span class="nt">&lt;/asp:Repeater&gt;</span>
  <span class="nt">&lt;/ItemTemplate&gt;</span>
  <span class="nt">&lt;FooterTemplate&gt;</span>
    <span class="nt">&lt;/ul&gt;</span>
  <span class="nt">&lt;/FooterTemplate&gt;</span>
<span class="nt">&lt;/asp:Repeater&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In lines 2-4, using the <code class="language-plaintext highlighter-rouge">HeaderTemplate</code> property of the <code class="language-plaintext highlighter-rouge">DateRepeater</code> DataRepeater control I create the initial ListView. It is then closed in the lines 21-24 using the <code class="language-plaintext highlighter-rouge">FooterTemplate</code> of the <code class="language-plaintext highlighter-rouge">DateRepeater</code> DataRepeater control. Next in the <code class="language-plaintext highlighter-rouge">ItemTemplate</code> property (Line 6-8) of the <code class="language-plaintext highlighter-rouge">DateRepeater</code> DataRepeater control, I create a list divider by adding a ListItem (LI) with the <code class="language-plaintext highlighter-rouge">data-role</code> of <code class="language-plaintext highlighter-rouge">list-divider</code> with a label for the date. Line 7 has a label control with a class of <code class="language-plaintext highlighter-rouge">ul-li-count</code> that holds the count of events that date. Now within the <code class="language-plaintext highlighter-rouge">ItemTemplate</code> property of the <code class="language-plaintext highlighter-rouge">DateRepeater</code> DataRepeater control I also have another DataRepeater, <code class="language-plaintext highlighter-rouge">EventRepeater.</code> This DataRepeater will iterate through all the events for a day and create the individual ListItems (LI) for each event on that day (Lines 11-17). Notice how the ListItem is wrapped in an <code class="language-plaintext highlighter-rouge">ASP:HyperLink</code> control. This will provide the hyperlink to the event details page. That sums up creating jQuery Mobile listview using an ASP.NET DataRepeater control.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="DataRepeater" /><category term="jQuery" /><category term="jQuery Mobile" /><category term="Web" /><summary type="html"><![CDATA[The jQuery Mobile framework has a list view widget that displays unordered lists in several different ways. In the process of converting the Microsoft Global MVP Summit mobile site to use jQuery Mobile, I wanted to change the way I displayed the events. The idea was to have a header for each date that there was an event along with some of the details of the event. Clicking on the event would take you the to event details page. To accomplish this I used the count bubble, list dividers, content formatting and the search filter bar features of jQuery Mobile. First let’s look at the HTML, for the sake of brevity I removed the ID fields from the HTML.]]></summary></entry><entry><title type="html">Converting an ASP.NET “Mobile” site to use jQuery Mobile</title><link href="https://www.josephguadagno.net/2011/12/31/converting-an-asp-net-mobile-site-to-use-jquery-mobile/" rel="alternate" type="text/html" title="Converting an ASP.NET “Mobile” site to use jQuery Mobile" /><published>2011-12-31T15:54:55-07:00</published><updated>2011-12-31T15:54:55-07:00</updated><id>https://www.josephguadagno.net/2011/12/31/converting-an-asp-net-mobile-site-to-use-jquery-mobile</id><content type="html" xml:base="https://www.josephguadagno.net/2011/12/31/converting-an-asp-net-mobile-site-to-use-jquery-mobile/"><![CDATA[<p>Building upon my previous post <a href="/2011/12/31/introduction-to-jquery-mobile/">Introduction to jQuery Mobile</a>, I wanted to share how I converted the <a href="https://www.mvpsummitevents.info/m/default.aspx" target="_blank">Microsoft Global MVP Summit mobile</a> site to use <a href="https://jquerymobile.com/" target="_blank">jQuery Mobile</a>.</p>

<h2 id="getting-started">Getting Started</h2>

<p>The first step was to look at the existing ASP.NET Master Page that the site was using and see where I had to make changes.  I included the skeleton mobile master page, prior to jQuery Mobile below.  Please note, I removed parts of the page that do not relate to the blog post.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;</span><span class="err">%@</span> <span class="na">Master</span> <span class="na">Language=</span><span class="s">"C#"</span>
  <span class="na">AutoEventWireup=</span><span class="s">"true"</span>
  <span class="na">CodeBehind=</span><span class="s">"Mobile.master.cs"</span>
  <span class="na">Inherits=</span><span class="s">"MVPSummitEvents.Website.Mobile"</span> <span class="err">%</span><span class="nt">&gt;</span>
<span class="cp">&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;</span>
<span class="nt">&lt;html</span> <span class="na">xmlns=</span><span class="s">"https://www.w3.org/1999/xhtml"</span> <span class="nt">&gt;</span>
<span class="nt">&lt;head</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span>MVP Summit Events - Mobile - <span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;asp:ContentPlaceHolder</span> <span class="na">ID=</span><span class="s">"head"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;/asp:ContentPlaceHolder&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;form</span> <span class="na">id=</span><span class="s">"form1"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mHead"</span><span class="nt">&gt;&lt;h1&gt;</span>MVP Summit Events<span class="nt">&lt;/h1&gt;&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mMenu"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;asp:HyperLink</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"hlMain"</span>
      <span class="na">NavigateUrl=</span><span class="s">"~/m/default.aspx"</span> <span class="na">Text=</span><span class="s">"Home"</span> <span class="na">ToolTip=</span><span class="s">"Home"</span> <span class="nt">/&gt;</span> |
    <span class="nt">&lt;asp:HyperLink</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"hlMap"</span>
      <span class="na">NavigateUrl=</span><span class="s">"~/m/map.aspx"</span> <span class="na">Text=</span><span class="s">"Map"</span> <span class="na">ToolTip=</span><span class="s">"Map"</span> <span class="nt">/&gt;</span> |
    <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://tinyurl.com/4e5mp5p"</span>
      <span class="na">title=</span><span class="s">"Windows Phone 7 App"</span><span class="nt">&gt;</span>Window Phone 7<span class="nt">&lt;/a&gt;</span> App. |
    <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://itunes.apple.com/us/app/mvp-events/id416291827?mt=8"</span><span class="nt">&gt;</span>iPhone<span class="nt">&lt;/a&gt;</span> App.
  <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mBody"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;asp:ContentPlaceHolder</span> <span class="na">ID=</span><span class="s">"CphBody"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;/asp:ContentPlaceHolder&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/form&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mFoot"</span><span class="nt">&gt;</span>Copyright ©2009-2012,
    <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://www.josephguadagno.net"</span> <span class="na">title=</span><span class="s">"Joseph Guadagno"</span><span class="nt">&gt;</span>Joseph Guadagno<span class="nt">&lt;/a&gt;</span> |
    <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://twitter.com/jguadagno"</span> <span class="na">title=</span><span class="s">"Follow jguadagno"</span><span class="nt">&gt;</span>@jguadagno<span class="nt">&lt;/a&gt;</span> |
    <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"mailto:jguadagno@hotmail.com"</span><span class="nt">&gt;</span>Contact<span class="nt">&lt;/a&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="converting-the-document-head">Converting the document head</h2>

<p>The first thing I needed to do was to update the DOCTYPE on line 3 to</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="cp">&lt;!DOCTYPE HTML&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Next was to include the jQuery Mobile scripts and CSS files, after that my head section looked like this:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;</span><span class="err">%@</span> <span class="na">Master</span> <span class="na">Language=</span><span class="s">"C#"</span>
  <span class="na">AutoEventWireup=</span><span class="s">"true"</span>
  <span class="na">CodeBehind=</span><span class="s">"Mobile.master.cs"</span>
  <span class="na">Inherits=</span><span class="s">"MVPSummitEvents.Website.Mobile"</span> <span class="err">%</span><span class="nt">&gt;</span>
<span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html</span> <span class="na">xmlns=</span><span class="s">"https://www.w3.org/1999/xhtml"</span> <span class="nt">&gt;</span>
<span class="nt">&lt;head</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;title&gt;</span>MVP Summit Events - Mobile - <span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span>
    <span class="na">href=</span><span class="s">"https://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span>
    <span class="na">src=</span><span class="s">"https://code.jquery.com/jquery-1.6.4.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span>
    <span class="na">src=</span><span class="s">"https://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  <span class="nt">&lt;asp:ContentPlaceHolder</span> <span class="na">ID=</span><span class="s">"HeadContent"</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;link</span> <span class="na">id=</span><span class="s">"Link1"</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">href=</span><span class="s">"/css/m.css"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> <span class="na">src=</span><span class="s">"../js/analytics.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> <span class="na">src=</span><span class="s">"../js/analytics.jqmobile.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;/head&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Again, I removed parts that are not required for this post.</p>

<p>You should notice that line 5 is now different and I added lines 9 - 15. Line 9 contains the viewport meta tag, the description from the jQuery Mobile docs is …</p>

<p>Note above that there is a meta <code class="language-plaintext highlighter-rouge">viewport</code> tag in the <code class="language-plaintext highlighter-rouge">head</code> to specify how the browser should display the page zoom level and dimensions. If this isn’t set, many mobile browsers will use a “virtual” page width around 900 pixels to make it work well with existing desktop sites but the screens may look zoomed out and too wide. By setting the viewport attributes to <code class="language-plaintext highlighter-rouge">content="width=device-width, initial-scale=1"</code>, the width will be set to the pixel width of the device screen.</p>

<p>Lines 10-15 contain the references to the jQuery Mobile style sheet, the jQuery library and the jQuery Mobile library. Line 19, is a workaround for using Google Analytics which is described <a href="https://www.jongales.com/blog/2011/01/10/google-analytics-and-jquery-mobile/" target="_blank">here</a>.</p>

<h2 id="converting-the-document-body">Converting the Document Body</h2>

<p>Looking at the body of Master Page, I was almost ready for the jQuery Mobile conversion.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mHead"</span><span class="nt">&gt;&lt;h1&gt;</span>MVP Summit Events<span class="nt">&lt;/h1&gt;&lt;/div&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mMenu"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;asp:HyperLink</span> <span class="na">runat=</span><span class="s">"server"</span>
    <span class="na">ID=</span><span class="s">"hlMain"</span>
    <span class="na">NavigateUrl=</span><span class="s">"~/m/default.aspx"</span>
    <span class="na">Text=</span><span class="s">"Home"</span> <span class="na">ToolTip=</span><span class="s">"Home"</span> <span class="nt">/&gt;</span> |
  <span class="nt">&lt;asp:HyperLink</span> <span class="na">runat=</span><span class="s">"server"</span>
    <span class="na">ID=</span><span class="s">"hlMap"</span> 
    <span class="na">NavigateUrl=</span><span class="s">"~/m/map.aspx"</span>
    <span class="na">Text=</span><span class="s">"Map"</span> <span class="na">ToolTip=</span><span class="s">"Map"</span> <span class="nt">/&gt;</span> |
  <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://tinyurl.com/4e5mp5p"</span>
    <span class="na">title=</span><span class="s">"Windows Phone 7 App"</span><span class="nt">&gt;</span>Window Phone 7<span class="nt">&lt;/a&gt;</span> App. |
  <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://itunes.apple.com/us/app/mvp-events/id416291827?mt=8"</span><span class="nt">&gt;</span>iPhone<span class="nt">&lt;/a&gt;</span> App.
<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mBody"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;asp:ContentPlaceHolder</span> <span class="na">ID=</span><span class="s">"CphBody"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;/asp:ContentPlaceHolder&gt;</span>
<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"mFoot"</span><span class="nt">&gt;</span>Copyright ©2009-2012,
  <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://www.josephguadagno.net"</span> <span class="na">title=</span><span class="s">"Joseph Guadagno"</span><span class="nt">&gt;</span>Joseph Guadagno<span class="nt">&lt;/a&gt;</span> |
  <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"https://twitter.com/jguadagno"</span> <span class="na">title=</span><span class="s">"Follow jguadagno"</span><span class="nt">&gt;</span>@jguadagno<span class="nt">&lt;/a&gt;</span> |
  <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"mailto:jguadagno@hotmail.com"</span><span class="nt">&gt;</span>Contact<span class="nt">&lt;/a&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I have my Master Page broken up into a header, menu, body, and footer. All I had to do was assign the jQuery Mobile data-role attributes and I was done. However, I took this time to “tweak” the layout of the mobile site look more like it would on the iPhone and other mobile devices. I still keep the header, but moved the menu to the footer section and moved the existing footer to an about page. The new body of the mobile Master Page looks like this.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"page"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"header"</span> <span class="na">data-theme=</span><span class="s">"b"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;h1&gt;&lt;asp:Label</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">ID=</span><span class="s">"HeaderLabel"</span><span class="nt">&gt;&lt;/asp:Label&gt;&lt;/h1&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"content"</span> <span class="na">data-theme=</span><span class="s">"b"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;asp:ContentPlaceHolder</span> <span class="na">ID=</span><span class="s">"BodyContent"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;/asp:ContentPlaceHolder&gt;</span>  
  <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"footer"</span> <span class="na">data-theme=</span><span class="s">"b"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"navbar"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;ul&gt;</span>
        <span class="nt">&lt;li&gt;&lt;asp:HyperLink</span> <span class="na">runat=</span><span class="s">"server"</span>
          <span class="na">NavigateUrl=</span><span class="s">"~/m/default.aspx"</span> <span class="na">data-icon=</span><span class="s">"home"</span><span class="nt">&gt;</span>Home<span class="nt">&lt;/asp:HyperLink&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;asp:HyperLink</span> <span class="na">runat=</span><span class="s">"server"</span>
          <span class="na">NavigateUrl=</span><span class="s">"~/m/map.aspx"</span> <span class="na">data-ajax=</span><span class="s">"false"</span><span class="nt">&gt;</span>Map<span class="nt">&lt;/asp:HyperLink&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;asp:HyperLink</span> <span class="na">runat=</span><span class="s">"server"</span>
          <span class="na">NavigateUrl=</span><span class="s">"~/m/Apps.aspx"</span><span class="nt">&gt;</span>Apps<span class="nt">&lt;/asp:HyperLink&gt;&lt;/li&gt;</span>
        <span class="nt">&lt;li&gt;&lt;asp:HyperLink</span> <span class="na">runat=</span><span class="s">"server"</span>
        <span class="na">NavigateUrl=</span><span class="s">"~/m/About.aspx"</span> <span class="na">data-icon=</span><span class="s">"info"</span><span class="nt">&gt;</span>About<span class="nt">&lt;/asp:HyperLink&gt;&lt;/li&gt;</span>
      <span class="nt">&lt;/ul&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>To me this seems quite readable and clearly identifies what each section (<code class="language-plaintext highlighter-rouge">div</code>) is responsible for.</p>

<p>Let’s break it down.</p>

<p>First, all the previous DIV’s in between the FORM tags where placed within a</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"page"</span><span class="nt">&gt;&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>tag. The next step was to convert each of the DIV’s that I had into a jQuery Mobile DIV. There is really no ‘jQuery Mobile DIV’ just an attribute that tells jQuery what “section” of the document this div is for. I added the <code class="language-plaintext highlighter-rouge">data-role=”header”</code> to the DIV with the ID of <code class="language-plaintext highlighter-rouge">mHead</code>, I added the <code class="language-plaintext highlighter-rouge">data-role=”content”</code> to the DIV with the ID of mBody and I added the <code class="language-plaintext highlighter-rouge">data-role=”footer”</code> to the DIV with the ID of <code class="language-plaintext highlighter-rouge">mFooter</code>.</p>

<p>“Ok, but what happened to the mMenu DIV,” you ask. Take a look at line 8 in the code sample above. I moved the “menu” to the footer of the new document and assigned it a <code class="language-plaintext highlighter-rouge">data-role</code> of the <code class="language-plaintext highlighter-rouge">navbar</code>. This is a jQuery Mobile widget that creates a simple menu of buttons out of all of the listitem elements of the unordered list. The data-icon attribute is an attribute that allows you to indicate one of the 18 built-in icons for any jQuery Mobile button.</p>

<p>After this, I was done. I now have a jQuery Mobile version of the Microsoft Global MVP Summit site that looks the same on virtually every mobile browser with little to no work.</p>

<p>Up next, how to convert other elements of an ASP.NET mobile site to use jQuery Mobile widgets.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="jQuery" /><category term="Web" /><summary type="html"><![CDATA[Building upon my previous post Introduction to jQuery Mobile, I wanted to share how I converted the Microsoft Global MVP Summit mobile site to use jQuery Mobile.]]></summary></entry><entry><title type="html">Introduction to jQuery Mobile</title><link href="https://www.josephguadagno.net/2011/12/31/introduction-to-jquery-mobile/" rel="alternate" type="text/html" title="Introduction to jQuery Mobile" /><published>2011-12-31T14:46:53-07:00</published><updated>2011-12-31T14:46:53-07:00</updated><id>https://www.josephguadagno.net/2011/12/31/introduction-to-jquery-mobile</id><content type="html" xml:base="https://www.josephguadagno.net/2011/12/31/introduction-to-jquery-mobile/"><![CDATA[<p>Back in November, I sat in on a session by <a href="https://www.hanselman.com/blog/" target="_blank">Scott Hanselman</a> on creating a mobile site on ASP.NET using <a href="https://jquerymobile.com/" target="_blank">jQuery Mobile</a>.  While I was watching this session I could not stop thinking how easy is this, I can do this for the mobile version of the <a href="https://mvpsummitevents.info/m/" target="_blank">Microsoft Global MVP Summit mobile</a> site. So like most of us, it took me a month to get to it.</p>

<p>A few days ago while on vacation and everyone was asleep, I started to play around with jQuery mobile and in about an hour I had a sample site created.  Here’s how you can get started.</p>

<h2 id="getting-started-with-jquery-mobile">Getting started with jQuery Mobile</h2>

<p>According the <a href="https://https://jquerymobile.com/" target="_blank">jQuery Mobile</a> website, jQuery Mobile is…</p>

<blockquote>
  <p>A unified, HTML5-based user interface system for all popular mobile device platforms, built on the rock-solid jQuery and jQuery UI foundation. Its lightweight code is built with progressive enhancement and has a flexible, easily themeable design</p>
</blockquote>

<p>To get started with jQuery Mobile you can head over to their <a href="https://jquerymobile.com/demos/1.0/docs/about/getting-started.html" target="_blank">Quick Start Guide</a>.  Here is the minimum HTML document that you need for jQuery Mobile.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
</pre></td><td class="rouge-code"><pre><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;title&gt;</span>My Page<span class="nt">&lt;/title&gt;</span> 
  <span class="nt">&lt;meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span>
    <span class="na">href=</span><span class="s">"https://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span>
    <span class="na">src=</span><span class="s">"https://code.jquery.com/jquery-1.6.4.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
  <span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span>
    <span class="na">src=</span><span class="s">"https://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"page"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"header"</span><span class="nt">&gt;</span>
     <span class="nt">&lt;h1&gt;</span>My Title<span class="nt">&lt;/h1&gt;</span>
    <span class="nt">&lt;/div&gt;</span><span class="c">&lt;!-- /header --&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"content"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;p&gt;</span>Hello world<span class="nt">&lt;/p&gt;</span>
    <span class="nt">&lt;/div&gt;</span><span class="c">&lt;!-- /content --&gt;</span>
  <span class="nt">&lt;/div&gt;</span><span class="c">&lt;!-- /page --&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The first this you will notice is the simple DOCTYPE.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="cp">&lt;!DOCTYPE html&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Next, you will need to include a reference to the jQuery Mobile scripts and CSS files in the HEAD section of the document.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> 
  <span class="na">href=</span><span class="s">"https://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> 
  <span class="na">src=</span><span class="s">"https://code.jquery.com/jquery-1.6.4.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
<span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"text/javascript"</span> 
  <span class="na">src=</span><span class="s">"https://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>And then finally in your HTML Body, you need to create a “page”. You do this by creating a DIV and an assigning the role of the page to it.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"#default"</span> <span class="na">data-role=</span><span class="s">"page"</span><span class="nt">&gt;</span>
...
<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The id is optional and can be used to have more than one page within a document.  For more on the page structure, check out the <a href="https://jquerymobile.com/demos/1.0/docs/pages/page-anatomy.html" target="_blank">Anatomy of a Page</a> in the jQuery Mobile docs.</p>

<p>That’s all you need for the bare minimal jQuery Mobile page.</p>

<h2 id="jquery-mobile-page-parts">jQuery Mobile Page Parts</h2>

<p>jQuery Mobile pages are made up of potentially 3 parts; the header, the content (body) and the footer.  These parts are outlined by creating a div and assigning a role to it.  The roles are <code class="language-plaintext highlighter-rouge">data-role=”header”</code>, <code class="language-plaintext highlighter-rouge">data-role=”content”</code>, and <code class="language-plaintext highlighter-rouge">data-role=”footer”</code>.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"page"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"header"</span><span class="nt">&gt;</span>...<span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"content"</span><span class="nt">&gt;</span>...<span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"footer"</span><span class="nt">&gt;</span>...<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="header-part">Header Part</h3>

<p>Is defined by the following markup.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"header"</span><span class="nt">&gt;</span>...<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The header part of the document is where you place content that you want as the head or beginning part of the page. This is typically page titles and toolbars.</p>

<h3 id="content-part">Content Part</h3>

<p>Is defined by the following markup.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"content"</span><span class="nt">&gt;</span>...<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The content part of the document is where you place the page content.</p>

<h3 id="footer-part">Footer Part</h3>

<p>Is defined by the following markup.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">data-role=</span><span class="s">"footer"</span><span class="nt">&gt;</span>...<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The footer part of the document is where you place the footer content. This is typically navigation, copyright information, etc.</p>

<p>Hopefully, I armed you with enough information to get started with jQuery Mobile. In the next few posts, I will talk about how I converted the <a href="https://www.mvpsummitevents.info" target="_blank">Microsoft Global MVP Summit</a> mobile site to use jQuery Mobile.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="jQuery" /><category term="Web" /><summary type="html"><![CDATA[Back in November, I sat in on a session by Scott Hanselman on creating a mobile site on ASP.NET using jQuery Mobile. While I was watching this session I could not stop thinking how easy is this, I can do this for the mobile version of the Microsoft Global MVP Summit mobile site. So like most of us, it took me a month to get to it.]]></summary></entry><entry><title type="html">40+</title><link href="https://www.josephguadagno.net/2011/10/18/40/" rel="alternate" type="text/html" title="40+" /><published>2011-10-17T19:17:00-07:00</published><updated>2011-10-17T19:17:00-07:00</updated><id>https://www.josephguadagno.net/2011/10/18/40</id><content type="html" xml:base="https://www.josephguadagno.net/2011/10/18/40/"><![CDATA[<p>So this blog post has been pending for over 4 months. I originally had the idea on June 1st, the day after I turned 40. It was to be titled <em>40 + 1</em>. The intent is for me to summarize the first 40 years of my life and outline what I am thinking about for the next 10. Here I (we) go …</p>

<p>Most of my life has been spent in New York in New York City, aka <em>“The City”</em>. I was born in Brooklyn, NY, raised in Queens, NY, moved to Long Island, NY when I was 22 then moved to Chandler, AZ when I was 35.</p>

<h2 id="school">School</h2>

<p>I attended a few different Elementary schools. One of which I qualified and participated for the City-wide spelling bee. I attended 1 Junior High School, where I was part of a program calls Stars, which was geared to “smarter” kids. This program allowed me to complete 3 school years in 2 (7th, 8th, and 9th grades). I then moved on to Richmond Hill High School where I was…</p>

<ul>
  <li>on the student council</li>
  <li>in charge of the student store</li>
  <li>coached the Junior Varsity Girls Basketball Team. We finished in 2nd place.</li>
  <li>swam on the school’s Varsity swim team.</li>
  <li>played on the Varsity Volleyball team. We made it to the City Championship but got eliminated. I did set a record for the most consecutive aces, 30.</li>
  <li>was a student counselor for troubled kids</li>
  <li>graduated with Cindy Lauper, out school gave her an honorary diploma since it was our 90th anniversary and she attended the school.</li>
  <li>graduated in the top ten of 400+.</li>
</ul>

<p>I attended both City College of New York (CCNY) and Long Island University. I did not graduate from either of the team.</p>

<h2 id="my-family-life">My family life</h2>

<p>In July of 1994, I met <a href="https://www.facebook.com/profile.php?id=1570265836" target="_blank">Deidre Faude</a>, after 2 years of dating we tied the knot. Our wedding was the happiest moment of my life… that was until I had my first child Joseph (JJ) and my second Emily. Having kids and watching them grow up and being active in their lives and keeping Dee happy is what I live for. Which leads me to my first life motto… <strong><em>I work to live, I don’t live to work</em></strong>.</p>

<p>For my kids, I am there when they need me, I have been a soccer coach for my son for 6 years and a baseball coach for 4. I have even coached his Chandler National All Star team last year. For my daughter, I have coached 4 of her softball teams, including her Chandler National All-Star team.</p>

<p>In the last two years, both of my parents have died. Yes, I used the word died and not lost, I know exactly where they are :smile:</p>

<h2 id="my-career-in-technology">My career in technology</h2>

<p>I attended college to become a High School Math teacher. Unfortunately, attending college and working full time both turned me off from the “education” system and turned me on to making money. My first job in technology was working as a Sales Person in a local Radio Shack. I then moved on to a manager position in an inventory company named RGIS. My first exposure to a technology job was when I worked for a store called ComputerWorld, which was a store that sold computers and software. One day a customer walked in and asked if I knew anyone that could write them some software for their real estate company. I said, that I could even though I knew nothing about programming at the time. :smile:</p>

<p>At this time, I went to my girl friend’s, at the time, college library and got a book on computer programming. It was the small blue book about QuickBasic. I started with the samples in this book and ended up building a quite large application in QuickBASIC for DOS. For this application, I had to write the user interface, the database (each table was a file), the database indexes, routines for rebuilding indexes, routines for printing and networking. Anyway, I eventually moved on to work for a real IT shop and quite a few other IT shops.</p>

<h2 id="community">Community</h2>

<p>I strongly believe in giving back to to the world (see my life motto <a href="#2">#2</a> below). In high school, I was a student peer counselor. At one point in college, I was actively participating on the MSN Board (yes MSN boards) where I got my first Microsoft MVP in Visual Basic (for DOS) back in 1996. It was not until I got married and had kids that I got back into the community. As I mentioned above, I coached both of kids teams in a few different sports. I even coached a soccer team when I first moved to Arizona for a friends of mines kid’s team. When I started working for my current company I wanted to give back even more. At this point, I joined the Big Brother Big Sister program where I was a big, both in age and weight :smile: for three different kids. It is definitely something I recommend everyone do at least once.</p>

<p>I got into the technology community back in 2008 when I started the <a href="https://sevdnug.org/home.aspx" target="_blank">Southeast Valley .NET User Group</a>. I presented times, helped out with other events which lead me to my second <a href="https://mvp.support.microsoft.com/profile=4C0083AE-C0DE-4F05-A179-D9072AF2EA2B" target="_blank">Microsoft MVP award</a>. However, that apparently was not enough and I joined the <a href="https://www.ineta.org" target="_blank">INETA</a>, International .NET Association Board of Directors where I became the Director of Marketing. After almost a year of service to INETA, I became the President of North America. Geez, that was a lot.</p>

<h2 id="the-next-10-years">The next 10 years</h2>

<p>In the next 10 years, both of my kids should attend and graduate college. I will hopefully make <a href="https://ineta.org" target="_blank">INETA</a> relevant again. I will make it to my 20 year wedding anniversary, if Dee can continue to put up with me :smile:.</p>

<p>No matter what, I will continue to live by my 2 mottos</p>

<ul>
  <li><em><strong>I work to life, I don’t live to work</strong></em></li>
  <li><em><strong>Leave this life better than it was when you entered it.</strong></em></li>
</ul>

<p>Where should I go next? What should I do?</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Personal" /><category term="Community" /><summary type="html"><![CDATA[So this blog post has been pending for over 4 months. I originally had the idea on June 1st, the day after I turned 40. It was to be titled 40 + 1. The intent is for me to summarize the first 40 years of my life and outline what I am thinking about for the next 10. Here I (we) go …]]></summary></entry><entry><title type="html">LightSwitch Meeting for SEVDNUG</title><link href="https://www.josephguadagno.net/2011/08/02/lightswitch-meeting-for-sevdnug/" rel="alternate" type="text/html" title="LightSwitch Meeting for SEVDNUG" /><published>2011-08-02T09:07:59-07:00</published><updated>2011-08-02T09:07:59-07:00</updated><id>https://www.josephguadagno.net/2011/08/02/lightswitch-meeting-for-sevdnug</id><content type="html" xml:base="https://www.josephguadagno.net/2011/08/02/lightswitch-meeting-for-sevdnug/"><![CDATA[<p>We are having a special user group meeting this month on <a href="https://msdn.microsoft.com/en-us/lightswitch?WT.mc_id=DT-MVP-4024623" target="_blank">LightSwitch</a> on <a href="https://sevdnug.org/Events/All/SEVDNUG_August_2011_Meeting_-_LightSwitch.aspx" target="_blank">August 25th</a> where <a href="https://blogs.msdn.com/b/bethmassi/" target="_blank">Beth Massi</a>, Senior Program Manager for BizApps, will present up to three sessions on LightSwitch.</p>

<p>The descriptions for the sessions are below. If you are interested in LightSwitch and want to attend please complete this <a href="https://www.esurveyspro.com/Survey.aspx?id=b8813313-91f2-452e-8e38-dc8f537ed363" target="_blank">survey</a>.</p>

<p>As usual, food and drinks will be provided for this event, as well as some extra special raffle prizes.  Some of the raffle prizes include new LightSwitch components from vendors like <a href="https://devexpress.com" target="_blank">Developer Express</a>, <a href="https://www.infragistics.com" target="_blank">Infragistics</a>, <a href="https://www.telerik.com" target="_blank">Telerik</a>, and <a href="https://www.componentone.com" target="_blank">ComponentOne</a>.  You might even win an MSDN license or a trip with Beth, myself and others to Friday nights Diamondback game.</p>

<h2 id="introducing-visual-studio-lightswitch-100-200">Introducing Visual Studio LightSwitch (100-200)</h2>

<p>Visual Studio LightSwitch is the simplest way to build business applications for the desktop and cloud. LightSwitch simplifies the development process by letting you concentrate on the business logic, while LightSwitch handles the common tasks for you. In this demo-heavy session, you will see, end-to-end, how to build and deploy a data-centric business application using LightSwitch.</p>

<h2 id="visual-studio-lightswitch---beyond-the-basics-200-300">Visual Studio LightSwitch - Beyond the Basics (200-300)</h2>

<p>LightSwitch is a new product in the Visual Studio family aimed at developers who want to easily create business applications for the desktop and the cloud. In this session, we’ll go beyond the basics of creating simple screens over data and demonstrate how to create screens with more advanced capabilities. You’ll see how to extend LightSwitch applications with your own Silverlight custom controls and RIA services. We’ll also talk about the architecture and additional extensibility points that are available to professional developers looking to enhance the LightSwitch developer experience.</p>

<h2 id="lightswitch-advanced-development-and-customization-techniques-300">LightSwitch Advanced Development and Customization Techniques (300+)</h2>

<p>Microsoft Visual Studio LightSwitch is the simplest way to build business applications for the desktop and the cloud. In the session, you will see advanced techniques for customizing the client and server pieces of a LightSwitch application. You will learn how to work with the LightSwitch API, build sophisticated UI, implement middle-tier application logic by customizing the data update pipeline, interact with Office applications, and much more without leaving the LightSwitch development environment. You will also see how combining Visual Studio Professional+ and your existing Silverlight and .NET skills provides even more customization opportunities by building LightSwitch extensions.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Community" /><category term="Events" /><category term="Arizona" /><summary type="html"><![CDATA[We are having a special user group meeting this month on LightSwitch on August 25th where Beth Massi, Senior Program Manager for BizApps, will present up to three sessions on LightSwitch.]]></summary></entry><entry><title type="html">SQL Pass Events Site Relaunched</title><link href="https://www.josephguadagno.net/2011/08/02/sql-pass-events-site-relaunched/" rel="alternate" type="text/html" title="SQL Pass Events Site Relaunched" /><published>2011-08-01T20:35:20-07:00</published><updated>2011-08-01T20:35:20-07:00</updated><id>https://www.josephguadagno.net/2011/08/02/sql-pass-events-site-relaunched</id><content type="html" xml:base="https://www.josephguadagno.net/2011/08/02/sql-pass-events-site-relaunched/"><![CDATA[<p>Are you going to <a href="https://www.sqlpass.org/summit/2011/" target="_blank">PASS Summit 2011</a> in October? If so, you might want to check out the updated site I launched called <a href="https://passsummitevents.info" target="_blank">PASS Summit Events</a>. PASS Summit Events will be your one stop shop for all of the events and after parties at PASS Summit 2011. Anyone can create an event on the site, all you need to do is sign in with your Windows Live credentials. Once signed in a link to create an event will appear. Please note, the event will not be available until it is reviewed and approved for display by an administrator. I added a few social media features like integration with Facebook and Twitter. A cleaner more consistent UI, bitly url shorten for sharing the event. And better support for Bing maps. Check out the <a href="https://passsummitevents.info" target="_blank">site</a>.</p>

<p><a href="/assets/images/posts/sqlpass.png"><img src="/assets/images/posts/sqlpass_thumb.png" alt="sqlpass" title="sqlpass" /></a></p>

<h2 id="staying-connected">Staying connected</h2>

<p>Don’t feel like checking out the site every day to see what is happening? Here are a few resources to help you.</p>

<ul>
  <li>The site <a href="https://www.passsummitevents.info/default.aspx" target="_blank">https://www.passsummitevents.info/default.aspx</a></li>
  <li>RSS Feed <a href="https://www.mvppasssummitevents.info/rss.ashx" target="_blank">https://www.passsummitevents.info/rss.ashx</a></li>
  <li>GeoRSS Feed <a href="https://www.passsummitevents.info/rss.ashx?format=geo" target="_blank">https://www.passsummitevents.info/rss.ashx?format=geo</a></li>
  <li>iCal Feed <a href="https://www.passsummitevents.info/calendar.ashx" target="_blank">https://www.passsummitevents.info/calendar.ashx</a></li>
  <li>Map of events <a href="https://www.passsummitevents.info/map.aspx" target="_blank">https://www.passsummitevents.info/map.aspx</a></li>
  <li>Flickr <a href="https://www.passsummitevents.info/flickr.aspx" target="_blank">https://www.passsummitevents.info/flickr.aspx</a></li>
</ul>

<h2 id="mobile-users">Mobile Users</h2>

<p>You will be able to view all of the events that are happening, even filter by day. In addition, you can view the events on a Map, and check out the event info.</p>

<ul>
  <li>Mobile Site <a href="https://www.passsummitevents.info/m/default.aspx" target="_blank">https://www.passsummitevents.info/m/default.aspx</a></li>
  <li>Mobile Map <a href="https://www.passsummitevents.info/m/m.aspx" target="_blank">https://www.passsummitevents.info/m/m.aspx</a></li>
  <li>Windows Phone 7 App <em>Not available</em></li>
  <li>iPhone and iPad App <a href="https://itunes.apple.com/us/app/mvp-events/id416291827?mt=8" target="_blank">https://itunes.apple.com/us/app/mvp-events/id416291827?mt=8</a></li>
</ul>

<h2 id="bellevue--seattle">Bellevue / Seattle</h2>

<p>In case you do not want to check out the events. Here are some other resources, also accessible from the site.</p>

<ul>
  <li>Bing Local - Bellevue <a href="https://www.bing.com/local/default.aspx?q=Bellevue%2C+WA&amp;go=&amp;form=LLSV" target="_blank">https://www.bing.com/local/default.aspx?q=Bellevue%2C+WA&amp;go=&amp;form=LLSV</a></li>
  <li>Bing Local - Seattle <a href="https://www.bing.com/local/default.aspx?q=Seattle%2C+WA&amp;go=&amp;form=LLSV" target="_blank">https://www.bing.com/local/default.aspx?q=Seattle%2C+WA&amp;go=&amp;form=LLSV</a></li>
  <li>Transportation - Bellevue <a href="https://www.ci.bellevue.wa.us/transportation.htm" target="_blank">https://www.ci.bellevue.wa.us/transportation.htm</a></li>
  <li>Transportation - Seattle <a href="https://www.seattle.gov/transportation/" target="_blank">https://www.seattle.gov/transportation/</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="News" /><category term="Events" /><category term="Community" /><summary type="html"><![CDATA[Are you going to PASS Summit 2011 in October? If so, you might want to check out the updated site I launched called PASS Summit Events. PASS Summit Events will be your one stop shop for all of the events and after parties at PASS Summit 2011. Anyone can create an event on the site, all you need to do is sign in with your Windows Live credentials. Once signed in a link to create an event will appear. Please note, the event will not be available until it is reviewed and approved for display by an administrator. I added a few social media features like integration with Facebook and Twitter. A cleaner more consistent UI, bitly url shorten for sharing the event. And better support for Bing maps. Check out the site.]]></summary></entry><entry><title type="html">My TechEd Video on Community</title><link href="https://www.josephguadagno.net/2011/06/03/my-teched-video-on-community/" rel="alternate" type="text/html" title="My TechEd Video on Community" /><published>2011-06-03T10:07:25-07:00</published><updated>2011-06-03T10:07:25-07:00</updated><id>https://www.josephguadagno.net/2011/06/03/my-teched-video-on-community</id><content type="html" xml:base="https://www.josephguadagno.net/2011/06/03/my-teched-video-on-community/"><![CDATA[<p>Back in May at TechEd North America <a href="https://www.devexpress.com" target="_blank">Developer Express</a> interviewed me about <a href="https://ineta.org" target="_blank">INETA</a>, Community and <a href="https://www.desertcodecamp.com" target="_blank">Desert Code Camp</a>. Check out the video.</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/vDtGk-P3soU" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<p>Thanks, Developer Express for your ongoing support of the community.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Community" /><category term="INETA" /><category term="Desert Code Camp" /><category term="TechEd" /><summary type="html"><![CDATA[Back in May at TechEd North America Developer Express interviewed me about INETA, Community and Desert Code Camp. Check out the video.]]></summary></entry><entry><title type="html">INETA and TechEd</title><link href="https://www.josephguadagno.net/2011/05/11/ineta-and-teched/" rel="alternate" type="text/html" title="INETA and TechEd" /><published>2011-05-11T11:56:54-07:00</published><updated>2011-05-11T11:56:54-07:00</updated><id>https://www.josephguadagno.net/2011/05/11/ineta-and-teched</id><content type="html" xml:base="https://www.josephguadagno.net/2011/05/11/ineta-and-teched/"><![CDATA[<p>Since being elected the President of <a href="https://www.ineta.org" target="_blank">INETA</a> North America in April, I have heard some good and bad things about INETA from several people.  I want to make INETA better and more relevant for people and user groups. That being said, I will be at Microsoft <a href="https://northamerica.msteched.com" target="_blank">Tech·Ed</a> in Atlanta next week along with the rest of the INETA board and would like to chat with people about the good, bad and ugly of INETA.  I will be</p>

<ul>
  <li>At the INETA Community Leadership Summit on Sunday</li>
  <li>The INETA Booth in the Connect Zone on the Expo floor at 12:30 pm daily for the INETA raffle.</li>
  <li>Community Night on Tuesday</li>
  <li>Walking throughout the convention center.</li>
</ul>

<p>If you have some compliments, complains, suggestions or just want to chat, here’s how you can get a hold of me:</p>

<ul>
  <li>Come to the <a href="https://inetateched2011.eventbrite.com/" target="_blank">INETA Community Leadership Summit</a> on Sunday.</li>
  <li>My TechEd <a href="https://northamerica.msteched.com/Directory/Details/a6434085-9918-4201-849e-8e6b2ad89337?fbid=R0PcL7ZnU9F" target="_blank">contact card</a>, contact me there or setup a meeting using that sites tool.</li>
  <li>Hit me up on Twitter at <a href="https://twitter.com/jguadagno" target="_blank">@jguadagno</a>.</li>
  <li>Look for me wearing my pink INETA shirt or my blue Polo shirt.</li>
  <li>Shoot me an email at <a href="mailto:joseph.guadagno@ineta.org" target="_blank">joseph.guadagno@ineta.org</a> to set up a meeting.</li>
</ul>

<p>BTW, <a href="https://www.ineta.org" target="_blank">INETA</a> is doing a lot this year at Microsoft <a href="https://northamerica.msteched.com" target="_blank">Tech·Ed</a>, check out the full list <a href="https://blog.ineta.org/post/INETA-at-Teche280a2Ed-2011.aspx" target="_blank">here</a>.</p>

<p>I look forward to chatting with you.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="News" /><category term="INETA" /><category term="Community" /><summary type="html"><![CDATA[Since being elected the President of INETA North America in April, I have heard some good and bad things about INETA from several people. I want to make INETA better and more relevant for people and user groups. That being said, I will be at Microsoft Tech·Ed in Atlanta next week along with the rest of the INETA board and would like to chat with people about the good, bad and ugly of INETA. I will be]]></summary></entry><entry><title type="html">Startup Weekend Chandler</title><link href="https://www.josephguadagno.net/2011/05/06/startup-weekend-chandler/" rel="alternate" type="text/html" title="Startup Weekend Chandler" /><published>2011-05-06T09:40:46-07:00</published><updated>2011-05-06T09:40:46-07:00</updated><id>https://www.josephguadagno.net/2011/05/06/startup-weekend-chandler</id><content type="html" xml:base="https://www.josephguadagno.net/2011/05/06/startup-weekend-chandler/"><![CDATA[<p>I wanted to let you know about an upcoming event called Startup Weekend Chandler. Startup Weekend is a non-profit, community-building event that brings together entrepreneurs of all backgrounds including software developers, marketers, designers, and other enthusiasts to start companies in just 54 hours. The participants that attend have 60 seconds to make a pitch (optional), the pitches are whittled down to the top ideas, and then teams form around the ideas to come out with several developed companies or projects. Finally, the weekend culminates with demonstrations in front of an audience of judges and potential investors.</p>

<p>This is Chandler’s first Startup Weekend, but the event occurs all over the world throughout the year and they have been very successful. The event will be held the weekend of June 24th and more info is at <a href="https://chandler.startupweekend.org/">https://chandler.startupweekend.org</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Arizona" /><category term="Community" /><category term="Events" /><summary type="html"><![CDATA[I wanted to let you know about an upcoming event called Startup Weekend Chandler. Startup Weekend is a non-profit, community-building event that brings together entrepreneurs of all backgrounds including software developers, marketers, designers, and other enthusiasts to start companies in just 54 hours. The participants that attend have 60 seconds to make a pitch (optional), the pitches are whittled down to the top ideas, and then teams form around the ideas to come out with several developed companies or projects. Finally, the weekend culminates with demonstrations in front of an audience of judges and potential investors.]]></summary></entry><entry><title type="html">Community Speaking and Cost</title><link href="https://www.josephguadagno.net/2011/05/05/community-speaking-and-cost/" rel="alternate" type="text/html" title="Community Speaking and Cost" /><published>2011-05-05T10:26:53-07:00</published><updated>2011-05-05T10:26:53-07:00</updated><id>https://www.josephguadagno.net/2011/05/05/community-speaking-and-cost</id><content type="html" xml:base="https://www.josephguadagno.net/2011/05/05/community-speaking-and-cost/"><![CDATA[<p>At the beginning of 2011, I set a goal for myself to speak at least twice at a community event that was outside of my area, the Phoenix metro area.  There are three reasons for this.</p>

<ol>
  <li>People here are probably sick of me talking :smile:</li>
  <li>I wanted to reach out to other audiences and get feedback on my presenting and talks</li>
  <li>The final, was / is a selfish one, to get my name out there</li>
</ol>

<p>I have spoken at <a href="https://www.socalcodecamp.com" target="_blank">SoCalCodeCamp</a> 5 times in the last two years and decided that does not count. So I went and submitted talks at <a href="https://codestock.org" target="_blank">CodeStock</a> in Nashville, TN. About a week of so ago, I had to do something that as an event organizer I do not like, I had to cancel my talk at <a href="https://www.codestock.org/" target="_blank">CodeStock</a>. It has nothing to do with the event, just the cost of me speaking there. This lead me to create a poll that I sent out via <a href="https://twitter.com/jguadagno" target="_blank">Twitter</a> and <a href="https://www.facebook.com/Joseph.J.Guadagno" target="_blank">Facebook</a>.</p>

<p>The poll asked a simple question, “<em>What is the most money you will spend out of pocket to speak at a community event? IE: Code Camp, User Groups, etc.”</em>. Although only 22 people responded, the result we pretty much spread evenly across the choices:</p>

<ul>
  <li>$100: 23%</li>
  <li>$200: 27%</li>
  <li>$300: 5%</li>
  <li>$500: 23%</li>
  <li>$1000: 18%</li>
</ul>

<p>Looking at the results of this scientific poll :smile:, I could not determine anything that I could really use to set the bar for myself.  However, some of the comments I received painted a totally different picture.</p>

<p>A friend of mine, Paul writes:</p>

<blockquote>
  <p>My take on this is that I am affected by my relationship to those who run the event as well (of course) to the proximity of the event. If the person(s) running the event are friends, I am more likely to attend at out-of-pocket cost. If I have no connection, then I am much less likely.</p>
</blockquote>

<p>This made me think a bit. I have spent probably $700 or so speaking at SoCalCodeCamp each time. So I asked myself why?  The answer is simple, <strong>Community</strong>. I love helping out and educating people, but one thing I really love is the social aspect of it.  As a result of speaking at SoCalCodeCamp, I have made some good friends in the Southern California area; <a href="https://blog.pewitt.org" target="_blank">Woody</a>, <a href="https://mrdenny.com" target="_blank">Denny</a>, <a href="https://twitter.com/scevans">Steve</a>, <a href="https://thesociablegeek.com/" target="_blank">Daniel</a>, <a href="https://davidmccarter.net" target="_blank">David</a> and more.  Now, every time Woody announces the next Code Camp I eagerly submit sessions and look forward to going to speak and hang out with my SoCal friends.</p>

<p>So to summarize, yes there is a cost to speaking outside your area, but there is the <em>potential</em> for making some good friends and expanding your reach and name.  So go and look for Code Camps, Days of .NET, or other community events to speak at and sign up.  Also, register yourself for the <a href="https://www.ineta.org/speakers" target="_blank">INETA Speakers Bureau</a> to help expand your reach.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Community" /><category term="Speaking" /><category term="Presentations" /><summary type="html"><![CDATA[At the beginning of 2011, I set a goal for myself to speak at least twice at a community event that was outside of my area, the Phoenix metro area.  There are three reasons for this.]]></summary></entry><entry><title type="html">TechEd Events Site Launched</title><link href="https://www.josephguadagno.net/2011/04/21/teched-events-site-launched/" rel="alternate" type="text/html" title="TechEd Events Site Launched" /><published>2011-04-21T08:22:42-07:00</published><updated>2011-04-21T08:22:42-07:00</updated><id>https://www.josephguadagno.net/2011/04/21/teched-events-site-launched</id><content type="html" xml:base="https://www.josephguadagno.net/2011/04/21/teched-events-site-launched/"><![CDATA[<p>Are you going to <a href="https://northamerica.msteched.com/myportal?fbid=R0PcL7ZnU9F" title="Tech·Ed North America 2011" target="_blank">Tech·Ed North America 2011</a> in May? If so, you might want to check out the updated site I launched called <a href="https://TechEdEvents.info" title="TechEd Events" target="_blank">Tech·Ed Events</a>. Tech·Ed Events will be your one stop shop for all of the events and after parties at Tech·Ed North America. Anyone can create an event on the site. All you need to do is sign in with your Windows Live credentials, and link to create an event will appear. Please note, the event will not be available until it is reviewed and approved for display. I added a few social media features like integration with Facebook and Twitter. A cleaner more consistent UI, bitly url shorten for sharing the event. And better support for Bing maps. Check out the <a href="https://www.techedevents.info" title="TechEd Events" target="_blank">site</a>. <a href="/assets/images/posts/image_2.png" target="_blank"><img src="/assets/images/posts/image_thumb_1.png" alt="image" title="image" /></a></p>

<h2 id="staying-connected">Staying connected</h2>

<p>Don’t feel like checking out the site every day to see what is happening? Here are a few resources to help you. The site <a href="https://www.techedevents.info/default.aspx" target="_blank">https://www.techedevents.info/default.aspx</a> RSS Feed <a href="https://www.techedevents.info/rss.ashx" target="_blank">https://www.techedevents.info/rss.ashx</a> GeoRSS Feed <a href="https://www.techedevents.info/rss.ashx?format=geo" target="_blank">https://www.techedevents.info/rss.ashx?format=geo</a> iCal Feed <a href="https://www.techedevents.info/calendar.ashx" target="_blank">https://www.techedevents.info/calendar.ashx</a> Map of events <a href="https://www.techedevents.info/map.aspx" target="_blank">https://www.techedevents.info/map.aspx</a> Flickr <a href="https://www.techedevents.info/flickr.aspx" target="_blank">https://www.techedevents.info/flickr.aspx</a></p>

<h2 id="mobile-users">Mobile Users</h2>

<p>You will be able to view all of the events that are happening, even filter by day. In addition, you can view the events on a Map, and check out the event info. Mobile Site <a href="https://www.techedevents.info/m/default.aspx" target="_blank">https://www.techedevents.info/m/default.aspx</a> Mobile Map <a href="https://www.techedevents.info/m/m.aspx" target="_blank">https://www.techedevents.info/m/m.aspx</a> Windows Phone 7 App <em>Coming Soon</em> iPhone and iPad App <a href="https://t.co/fgttFEn" title="https://t.co/fgttFEn" target="_blank">https://t.co/fgttFEn</a></p>

<h2 id="atlanta">Atlanta</h2>

<p>In case you do not want to check out the events. Here are some other resources, also accessible from the site.</p>

<ul>
  <li>Bing Local – Atlanta <a href="https://www.bing.com/local/default.aspx?q=Atlanta%2C+GA&amp;go=&amp;form=LLSV" title="https://www.bing.com/local/default.aspx?q=Atlanta%2C+GA&amp;go=&amp;form=LLSV" target="_blank">https://www.bing.com/local/default.aspx?q=Atlanta%2C+GA&amp;go=&amp;form=LLSV</a></li>
  <li>Transportation - Atlanta MARTA <a href="https://www.atlanta.com/marta.php" title="https://www.atlanta.com/marta.php" target="_blank">https://www.atlanta.com/marta.php</a></li>
  <li></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Technology News" /><category term="Events" /><category term="Community" /><summary type="html"><![CDATA[Are you going to Tech·Ed North America 2011 in May? If so, you might want to check out the updated site I launched called Tech·Ed Events. Tech·Ed Events will be your one stop shop for all of the events and after parties at Tech·Ed North America. Anyone can create an event on the site. All you need to do is sign in with your Windows Live credentials, and link to create an event will appear. Please note, the event will not be available until it is reviewed and approved for display. I added a few social media features like integration with Facebook and Twitter. A cleaner more consistent UI, bitly url shorten for sharing the event. And better support for Bing maps. Check out the site.]]></summary></entry><entry><title type="html">Book Review: ASP.NET Site Performance Secrets</title><link href="https://www.josephguadagno.net/2011/03/25/book-review-asp-net-site-performance-secrets/" rel="alternate" type="text/html" title="Book Review: ASP.NET Site Performance Secrets" /><published>2011-03-25T11:14:00-07:00</published><updated>2011-03-25T11:14:00-07:00</updated><id>https://www.josephguadagno.net/2011/03/25/book-review-asp-net-site-performance-secrets</id><content type="html" xml:base="https://www.josephguadagno.net/2011/03/25/book-review-asp-net-site-performance-secrets/"><![CDATA[<p>When I first got this book I thought it was going to be a compilation of tips and tricks. I was quite surprised with the fact that it really wasn’t.  The author starts out by telling you how you to determine the bottlenecks in your site then goes into a lot of samples on how to correct and / or improve them. He covers memory, CPU, caching, compression and more.</p>

<p>This book is definitely worth a read then a reread every once in a while. I plan on implementing some of the suggestions for my sites.</p>

<p>Check out the book on the <a href="https://www.packtpub.com/asp-net-site-performance-secrets/book" target="_blank">Packt Publishing</a> site or <a href="https://www.amazon.com/gp/product/1849690685/ref=as_li_ss_tl?ie=UTF8&amp;tag=beyondthebasic0e&amp;linkCode=as2&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1849690685" target="_blank">ASP.NET Site Performance Secrets</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[When I first got this book I thought it was going to be a compilation of tips and tricks. I was quite surprised with the fact that it really wasn’t.  The author starts out by telling you how you to determine the bottlenecks in your site then goes into a lot of samples on how to correct and / or improve them. He covers memory, CPU, caching, compression and more.]]></summary></entry><entry><title type="html">Party Bus from LA to Chandler for Desert Code Camp</title><link href="https://www.josephguadagno.net/2011/03/11/party-bus-from-la-to-chandler-for-desert-code-camp/" rel="alternate" type="text/html" title="Party Bus from LA to Chandler for Desert Code Camp" /><published>2011-03-11T07:16:00-07:00</published><updated>2011-03-11T07:16:00-07:00</updated><id>https://www.josephguadagno.net/2011/03/11/party-bus-from-la-to-chandler-for-desert-code-camp</id><content type="html" xml:base="https://www.josephguadagno.net/2011/03/11/party-bus-from-la-to-chandler-for-desert-code-camp/"><![CDATA[<p>Back in January at <a href="https://www.socalcodecamp.com/" target="_blank">SoCal Code Camp</a> Fullerton, <a href="https://TheSociableGeek.com" target="_blank">Daniel Egan</a>, <a href="https://woodyp.info" target="_blank">Woody Pewitt</a> and I were chatting about the different camps and how can we collaborate better. That’s where the idea of the party bus can up.  We said, “What about getting a bus from LA to go to Chandler for Desert Code Camp?” If it was successful, we would do the same for the next <a href="https://www.socalcodecamp.com/" target="_blank">SoCal Code Camp</a> in San Diego.</p>

<p>That being said, come and join the party bus from Los Angeles County to <a href="https://apr2011.desertcodecamp.com" target="_blank">Desert Code Camp</a>. The tickets are $60 round trip. The location will be announced shortly, it will most likely be in Riverside or Ontario.  Drinks and snack will be provided on the bus. Reserve your seat <a href="https://la2dcc2011.eventbrite.com" target="_blank">here</a>.</p>

<p>If you are worried about where you are going to stay, don’t. The organizers of Desert Code Camp have partnered up with the <a href="https://www.sanmarcosresort.com/" target="_blank">San Marcos Crowne Plaza</a> in downtown Chandler, AZ to bring you a  great hotel deal. Please visit the Desert Code Camp / San Marcos Crowne Plaza reservation <a href="https://resweb.passkey.com/Resweb.do?mode=welcome_ei_new&amp;eventID=3324014&amp;utm_source=55438&amp;utm_medium=email&amp;utm_campaign=4565629" target="_blank">page</a> to make your reservation.   Please note that the reservation for the hotel is separate from that of the bus ticket and you will be responsible for both.</p>

<p>You can check out the sessions for Desert Code Camp 2011.1 at <a href="https://apr2011.desertcodecamp.com/sessions" target="_blank">https://apr2011.desertcodecamp.com/sessions</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Technology News" /><category term="Desert Code Camp" /><category term="Arizona" /><category term="Community" /><summary type="html"><![CDATA[Back in January at SoCal Code Camp Fullerton, Daniel Egan, Woody Pewitt and I were chatting about the different camps and how can we collaborate better. That’s where the idea of the party bus can up. We said, “What about getting a bus from LA to go to Chandler for Desert Code Camp?” If it was successful, we would do the same for the next SoCal Code Camp in San Diego.]]></summary></entry><entry><title type="html">Announcing Desert Code Camp 2011.1</title><link href="https://www.josephguadagno.net/2011/01/19/announcing-desert-code-camp-2011-1/" rel="alternate" type="text/html" title="Announcing Desert Code Camp 2011.1" /><published>2011-01-18T21:24:00-07:00</published><updated>2011-01-18T21:24:00-07:00</updated><id>https://www.josephguadagno.net/2011/01/19/announcing-desert-code-camp-2011-1</id><content type="html" xml:base="https://www.josephguadagno.net/2011/01/19/announcing-desert-code-camp-2011-1/"><![CDATA[<p>I am pleased to announce the date for Desert Code Camp 2011.1, aka April 2011 code camp. It will take place on Saturday, April, 2nd, 2010.</p>

<p>The event will still take place at the wonderful Chandler-Gilbert Community College - <a href="https://www.cgc.maricopa.edu/adminservices/maps/pecos/Pages/PecosCampus.aspx" target="_blank">Pecos</a> Campus.<br />
<strong>Speaker Dinner</strong><br />
We will have a speaker dinner also at this Desert Code Camp. The speaker dinner will take place on Friday night, <em>pending a sponsor</em>. More information will be provided when we get closer.<br />
<strong>After Party</strong><br />
We will have an after party in Downtown Chandler after the Code Camp (pending a sponsor). Downtown Chandler is approximately 3 miles away from Chandler-Gilbert Community College.<br />
<strong>Breakfast</strong><br />
Breakfast will be provided at Desert Code Camp (pending a sponsor).<br />
<strong>Lunch</strong><br />
Lunch will be provided at Desert Code Camp (pending a sponsor).<br />
If you want to help or sponsor, shoot me an email at jguadagno@hotmail.com.</p>

<h2 id="web-site-updates">Web Site Updates</h2>

<ul>
  <li>We made several security improvements to the site.</li>
  <li>We added a new badge widget to the site. Now you can showcase the classed you are attending or the ones that you are speaking at. Check it out at <a href="https://apr2011.desertcodecamp.com/badge.aspx" target="_blank">https://apr2011.desertcodecamp.com/badge.aspx</a></li>
  <li>Cleaned up the user interface. Most pages are very consistent and there is extensive use of jQuery and jQuery themes.</li>
  <li>Published <a href="https://apr2011.desertcodecamp.com/feeds.ashx" target="_blank">RSS</a>, <a href="https://apr2011.desertcodecamp.com/feeds.ashx?format=atom" target="_blank">Atom</a> and <a href="https://apr2011.desertcodecamp.com/ical.ashx" target="_blank">iCal</a> feeds.<br />
You can customize the iCal feed with your sessions by adding?LoginId=<em>loginId</em> to the end of the URL.</li>
  <li>The sessions listing is cleaner with an accordion look.</li>
  <li>You can tweet or Like any page.</li>
  <li>You can now, add Facebook comments to any session</li>
  <li>Users can add there Twitter Profile, Speaker Rate Id, facebook Id, Slide Share Id, and website to their profile.</li>
  <li>Speakers can add a URL to the source code, video links, slide rule, and slide share URL for the site.</li>
  <li>Added a new feedback module powered by UserVoice. You can add feedback for the site directly from the site or at <a href="https://desertcodecamp.uservoice.com" target="_blank">https://desertcodecamp.uservoice.com</a></li>
</ul>

<h2 id="get-social">Get Social</h2>

<ul>
  <li>Desert Code Camp twitter account: <a href="https://twitter.com/DesertCodeCamp" target="_blank">@DesertCodeCamp</a></li>
  <li>Desert Code Camp on Facebook: <a href="https://facebook.com/DesertCodeCamp" target="_blank">https://facebook.com/DesertCodeCamp</a></li>
</ul>

<p>Start submitting your sessions today at <a href="https://apr2011.desertcodecamp.com" target="_blank">https://apr2011.desertcodecamp.com</a></p>

<p>If you are looking to help sponsor, please check out the <a href="https://apr2011.desertcodecamp.com/docs/apr2011_sponsorship.pdf" target="_blank">sponsorship document</a>.</p>

<p>Look forward to seeing you there!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Desert Code Camp" /><category term="Arizona" /><category term="Community" /><summary type="html"><![CDATA[I am pleased to announce the date for Desert Code Camp 2011.1, aka April 2011 code camp. It will take place on Saturday, April, 2nd, 2010.]]></summary></entry><entry><title type="html">MVP Summit Events Site Relaunched</title><link href="https://www.josephguadagno.net/2010/12/16/mvp-summit-events-site-relaunched/" rel="alternate" type="text/html" title="MVP Summit Events Site Relaunched" /><published>2010-12-16T10:52:00-07:00</published><updated>2010-12-16T10:52:00-07:00</updated><id>https://www.josephguadagno.net/2010/12/16/mvp-summit-events-site-relaunched</id><content type="html" xml:base="https://www.josephguadagno.net/2010/12/16/mvp-summit-events-site-relaunched/"><![CDATA[<p>Are you going to <a href="https://2011mvpsummit.com" target="_blank">Microsoft 2011 Global MVP Summit</a> in February? If so, you might want to check out the updated site I launched called <a href="https://mvpsummitevents.info" target="_blank">MVP Summit Events</a>. MVP Summit Events will be your one stop shop for all of the events and after parties at MVP11. Anyone can create an event on the sign. All you need to do is sign in with you Windows Live credentials, and link to create an event will appear. Please note, the event will not be available until it is reviewed and approved for display. I added a few new social media features like integration with Facebook and Twitter. A cleaner more consistent UI, bitly url shorten for sharing the event. And better support for Bing maps. Check out the <a href="https://mvpsummitevents.info" target="_blank">site</a>. <a href="https://mvpsummitevents.info" title="MVP Summit Events" target="_blank"><img src="/assets/images/posts/image_1.png" alt="MVPSummitEvents.info" title="MVPSummitEvents.info" /></a></p>

<h2 id="staying-connected">Staying connected</h2>

<p>Don’t feel like checking out the site every day to see what is happening? Here are a few resources to help you.</p>

<table>
  <thead>
    <tr>
      <th>Site</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>The site</td>
      <td><a href="https://www.mvpsummitevents.info/default.aspx" target="_blank">https://www.mvpsummitevents.info/default.aspx</a></td>
    </tr>
    <tr>
      <td>RSS Feed</td>
      <td><a href="https://www.mvpsummitevents.info/rss.ashx" target="_blank">https://www.mvpsummitevents.info/rss.ashx</a></td>
    </tr>
    <tr>
      <td>GeoRSS Feed</td>
      <td><a href="https://www.mvpsummitevents.info/rss.ashx?format=geo" target="_blank">https://www.mvpsummitevents.info/rss.ashx?format=geo</a></td>
    </tr>
    <tr>
      <td>iCal Feed</td>
      <td><a href="https://www.mvpsummitevents.info/calendar.ashx" target="_blank">https://www.mvpsummitevents.info/calendar.ashx</a></td>
    </tr>
    <tr>
      <td>Map of events</td>
      <td><a href="https://www.mvpsummitevents.info/map.aspx" target="_blank">https://www.mvpsummitevents.info/map.aspx</a></td>
    </tr>
    <tr>
      <td>Flickr</td>
      <td><a href="https://www.mvpsummitevents.info/flickr.aspx" target="_blank">https://www.mvpsummitevents.info/flickr.aspx</a></td>
    </tr>
  </tbody>
</table>

<h2 id="mobile-users">Mobile Users</h2>

<p>You will be able to view all of the events that are happening, even filter by day. In addition, you can view the events on a Map, and check out the event info.</p>

<table>
  <thead>
    <tr>
      <th>Site</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Mobile Site</td>
      <td><a href="https://www.mvpsummitevents.info/m/default.aspx" target="_blank">https://www.mvpsummitevents.info/m/default.aspx</a></td>
    </tr>
    <tr>
      <td>Mobile Map</td>
      <td><a href="https://www.mvpsummitevents.info/m/m.aspx" target="_blank">https://www.mvpsummitevents.info/m/m.aspx</a></td>
    </tr>
    <tr>
      <td>Windows Phone 7 App</td>
      <td><a href="https://tinyurl.com/4e5mp5p" title="Windows Phone 7 Application" target="_blank">https://tinyurl.com/4e5mp5p</a></td>
    </tr>
    <tr>
      <td>iPhone and iPad App</td>
      <td><a href="https://itunes.apple.com/us/app/mvp-events/id416291827?mt=8" title="iPad and iPhone Application" target="_blank">https://itunes.apple.com/us/app/mvp-events/id416291827?mt=8</a></td>
    </tr>
  </tbody>
</table>

<h2 id="bellevue--seattle">Bellevue / Seattle</h2>

<p>In case you do not want to check out the events. Here are some other resources, also accessible from the site.</p>

<table>
  <thead>
    <tr>
      <th>Site</th>
      <th>Url</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bing Local - Bellevue</td>
      <td><a href="https://www.bing.com/local/default.aspx?q=Bellevue%2C+WA&amp;go=&amp;form=LLSV" title="https://www.bing.com/local/default.aspx?q=Bellevue%2C+WA&amp;go=&amp;form=LLSV" target="_blank">https://www.bing.com/local/default.aspx?q=Bellevue%2C+WA&amp;go=&amp;form=LLSV</a></td>
    </tr>
    <tr>
      <td>Bing Local - Seattle</td>
      <td><a href="https://www.bing.com/local/default.aspx?q=Seattle%2C+WA&amp;go=&amp;form=LLSV" title="https://www.bing.com/local/default.aspx?q=Seattle%2C+WA&amp;go=&amp;form=LLSV" target="_blank">https://www.bing.com/local/default.aspx?q=Seattle%2C+WA&amp;go=&amp;form=LLSV</a></td>
    </tr>
    <tr>
      <td>Transportation - Bellevue</td>
      <td><a href="https://www.ci.bellevue.wa.us/transportation.htm" title="https://www.ci.bellevue.wa.us/transportation.htm" target="_blank">https://www.ci.bellevue.wa.us/transportation.htm</a></td>
    </tr>
    <tr>
      <td>Transportation - Seattle</td>
      <td><a href="https://www.seattle.gov/transportation/" title="https://www.seattle.gov/transportation/" target="_blank">https://www.seattle.gov/transportation/</a></td>
    </tr>
  </tbody>
</table>]]></content><author><name>Joseph Guadagno</name></author><category term="Technology News" /><category term="Events" /><category term="Community" /><category term="Microsoft" /><category term="MVP" /><summary type="html"><![CDATA[Are you going to Microsoft 2011 Global MVP Summit in February? If so, you might want to check out the updated site I launched called MVP Summit Events. MVP Summit Events will be your one stop shop for all of the events and after parties at MVP11. Anyone can create an event on the sign. All you need to do is sign in with you Windows Live credentials, and link to create an event will appear. Please note, the event will not be available until it is reviewed and approved for display. I added a few new social media features like integration with Facebook and Twitter. A cleaner more consistent UI, bitly url shorten for sharing the event. And better support for Bing maps. Check out the site.]]></summary></entry><entry><title type="html">Thinking of starting a User Group</title><link href="https://www.josephguadagno.net/2010/10/26/thinking-of-starting-a-user-group/" rel="alternate" type="text/html" title="Thinking of starting a User Group" /><published>2010-10-25T22:56:39-07:00</published><updated>2010-10-25T22:56:39-07:00</updated><id>https://www.josephguadagno.net/2010/10/26/thinking-of-starting-a-user-group</id><content type="html" xml:base="https://www.josephguadagno.net/2010/10/26/thinking-of-starting-a-user-group/"><![CDATA[<p>A while ago I was asked by a friend of mine in SoCal (Southern California) about how to start a user group. After answering his email I decided it would be nice to have it in a blog post, so here it is.</p>

<p>There are a lot of components to starting a user group.  The biggest are location, time and theme and keeping them consistent. <strong><em>Consistency</em></strong> is important to a successful user group.</p>

<p><strong>Your location should be consistent</strong>. It is OK if you are starting and trying to find a new place to settle in, but once you settle in, keep it that way. Try not to bounce from place to place.</p>

<p><strong>The time you meet needs to be consistent</strong>.  Have it at the same time of the month. Ex: 4th Thursday of the month, 2nd Wednesday. Try to keep in mind other user groups in the area (I know SoCal has tons) and when they meet.  It is difficult for most people to go out more than once a week.</p>

<p><strong>The theme should be consistent also</strong>.  Are you going to be focused around Azure, C#, General .NET, Cloud, etc. See if there are other user groups in the area with the same interest.</p>

<p><strong>Another thing to keep in mind is communication</strong>. Try to get a {:target=”_blank”}(my user group) so that people can remember it.  In the beginning, you will need to send out a lot to build up the group.</p>

<p><strong>Try to solicit help</strong>. Starting and maintaining a user group is difficult to do especially for one person. Try to get other people to help you.</p>

<p><strong>Sponsors</strong> Getting sponsorship is hard, especially in beginning, until you have the contacts. Most local recruiting companies are willing to help sponsor pizza and such for a meeting.  Most third-party vendors have contacts in their marketing department on their sites.  They are always willing to provide SWAG.  You can also check out the offers page on the <a href="https://ineta.org/offers/" target="_blank">INETA</a> site.</p>

<p><strong>Additional Help</strong> Check with your local Microsoft developer <a href="https://msdn.microsoft.com/en-us/bb905078.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">evangelist</a> they should be able to provide some help to get you started. Also look at the INETA <a href="https://ineta.org/MembershipTeam.aspx" target="_blank">Membership</a> mentors to help get you started.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Community" /><category term="User Group" /><summary type="html"><![CDATA[A while ago I was asked by a friend of mine in SoCal (Southern California) about how to start a user group. After answering his email I decided it would be nice to have it in a blog post, so here it is.]]></summary></entry><entry><title type="html">Desert Code Camp 2010.2 Sessions so far</title><link href="https://www.josephguadagno.net/2010/09/17/desert-code-camp-2010-2-sessions-so-far/" rel="alternate" type="text/html" title="Desert Code Camp 2010.2 Sessions so far" /><published>2010-09-17T14:30:41-07:00</published><updated>2010-09-17T14:30:41-07:00</updated><id>https://www.josephguadagno.net/2010/09/17/desert-code-camp-2010-2-sessions-so-far</id><content type="html" xml:base="https://www.josephguadagno.net/2010/09/17/desert-code-camp-2010-2-sessions-so-far/"><![CDATA[<p>Here is the list of sessions that have been submitted and approved as of September 17th, 2010. It even includes 3 sessions for our Gangplank Jr. track for kids.</p>

<p>Take a look, if you want to present, just add a session, sign in, go to the sessions’ detail page and choose, “<em>I can teach this</em>”.</p>

<p><a href="https://nov2010.desertcodecamp.com" title="Desert Code Camp" target="_blank">https://nov2010.desertcodecamp.com</a></p>

<table>
  <thead>
    <tr>
      <th>Track</th>
      <th>Session</th>
      <th>Abstract</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>.NET Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/106" target="_blank">Code First Entity Framework Development</a></td>
      <td>The objective of this session is to introduce the code-first framework for Microsoft’s Entity Framework technology. I’ll introduce the code first framework, and demonstrate how to build applications that access databases using the code first approach. I’ll demonstrate using the code-first library in an enterprise, web, and desktop application.</td>
    </tr>
    <tr>
      <td>.NET Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/107" target="_blank">Extensible Applications</a></td>
      <td>The objective of this presentation is to demonstrate how to create extensible applications using the Microsoft .NET Framework. During this presentation, we will explore using the Managed Extensibility Framework, Managed Add-in Framework, Workflow Foundation, and Dynamic Language Runtime to allow after-market customizations and extensions to your applications.</td>
    </tr>
    <tr>
      <td>.NET Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/112" target="_blank">.NET 4 Parallel Programming in C#</a></td>
      <td>parallel programming in .NET 4.0</td>
    </tr>
    <tr>
      <td>.NET Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/124" target="_blank">Introducing Workflow Services in WF 4.0</a></td>
      <td>There are many benefits to model-driven design, including transparency of design for multiple audiences and the simplification of low-level patterns which assist in realizing application designs that are simple to implement and understand. Come learn how simple it is to build workflow services with Windows Workflow Foundation (WF) 4.0.</td>
    </tr>
    <tr>
      <td>.NET Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/142" target="_blank">Back-2-Basics: Exception &amp; Event Instrumentation in .NET</a></td>
      <td>This session will instruct any level of programmer on how to easily use tracing that is built into .NET to log and analyze Exceptions and events that occur during application runtime. This is invaluable to fix bugs that only happen while the application is running. .NET TraceListeners will be discussed in detail along with how to write your own custom TraceListeners. I will also discuss and provide code for my centralized exception/ event logging system that allows applications at customer sites or on multiple servers to log to central database. Developers or technical support personal can then view these entries via an ASP.NET web site.</td>
    </tr>
    <tr>
      <td>.NET Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/143" target="_blank">Back-2-Basics: .NET Coding Standards For The Real World</a></td>
      <td>This session will guide any level of programmer to greater productivity by providing the information needed to write consistent, maintainable code. Learn about project setup, assembly layout, code style, defensive programming and much, much more. We will even go over some real in production code and see what the programmer did wrong in “What’s Wrong With this Code?”. Code tips are included to help you write better, error-free applications. Lots of code examples in C# and VB.NET.</td>
    </tr>
    <tr>
      <td>AJAX / Javascript</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/135" target="_blank">AJAX with jQuery</a></td>
      <td>In this session, you’ll learn various techniques for adding AJAX to your site using jQuery. There will be a very brief introduction to jQuery for anyone who hasn’t used it before.</td>
    </tr>
    <tr>
      <td>AJAX / Javascript</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/119" target="_blank">Javascript Bootcamp</a></td>
      <td>Stop writing bad JavaScript Now! Learn to avoid mistakes which lead to performance and scalability problems with your web applications. Why is the global namespace a problem? What is a closure? Can js implement OO patterns? All this and more…</td>
    </tr>
    <tr>
      <td>Architecture</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/147" target="_blank">Enterprise Architecture and TOGAF</a></td>
      <td>The purpose of this session is to show how EA helps to business objectives.</td>
    </tr>
    <tr>
      <td>Business Intelligence</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/111" target="_blank">Practical applicability of PowerPivot</a></td>
      <td>The goal of this session is to show how PowerPivot can be used to combine data sources, visualize data, and create KPIs in the least amount of code.</td>
    </tr>
    <tr>
      <td>Cloud</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/116" target="_blank">Azure Security Practices</a></td>
      <td>This presentation will walk through many of the common questions people have regarding security and the Azure cloud. Topics covered will specifically include compliance, database security, data sensitivity, and the options you have to mitigate and compensate for them. Emerging &amp; applicable best practices in cloud computing will be outlined.</td>
    </tr>
    <tr>
      <td>Composite Application Servers</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/123" target="_blank">Building Composite Application Services with Windows Server AppFabric</a></td>
      <td>Windows Server AppFabric extends the core capabilities of IIS by providing many of the cloud benefits on-premise including elastic scale and robust hosting capabilities. Come learn how Windows AppFabric can benefit your approach to building and supporting composite application services via enhanced lifetime management, tracking, persistence of long-running workflow services and caching for performance optimization.</td>
    </tr>
    <tr>
      <td>Continuous Integration with Hudson</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/148" target="_blank">Continuous Integration with Hudson</a></td>
      <td>Use Hudson to help provide your team with continuous feedback on the state of your project. This session will teach you how to install Hudson and common techniques for using Hudson to keep you informed.</td>
    </tr>
    <tr>
      <td>Databases (SQL/RDBMS, Document, Object)</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/138" target="_blank">NoSQL Databases</a></td>
      <td>NoSQL Databases</td>
    </tr>
    <tr>
      <td>Entity Framework 4.0</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/149" target="_blank">MVC and Entity Framework</a></td>
      <td>The introduction of MVC to the ASP.NET stack opened up a new paradigm for building web sites and web applications and, this new way of thinking leads to new ways to interact with your data. Microsoft has been promoting the Entity Framework, which many people feel is too abstract, esoteric, too much overkill, or just too darn difficult to learn. In this presentation, James will dispel all the myths and tabloid rumors you may have heard about EF and take you on a gentle dive into the following: • A brief history of EF • How EF becomes the mediator between developers and DBAs • How to build an EF Model • Wiring up the Model to your MVC app • The supporting role of repositories • Using LINQ to EF to get to your data • How to win friends and influence people by embracing EF</td>
    </tr>
    <tr>
      <td>Game Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/125" target="_blank">Mobile Game Development 101</a></td>
      <td>Developing simple games for the iPhone and Android to get you started.</td>
    </tr>
    <tr>
      <td>Gangplank Jr</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/154" target="_blank">Game Development with Scratch</a></td>
      <td>Develop an old school game like Galaga using Scratch.</td>
    </tr>
    <tr>
      <td>Gangplank Jr</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/155" target="_blank">Android Phone Development</a></td>
      <td>Expanding on the building block style programming of Scratch use Google App Inventor to build and deploy an Android Application.</td>
    </tr>
    <tr>
      <td>Gangplank Jr</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/156" target="_blank">Lego Mindstorms</a></td>
      <td>Create a battle bot using the Lego Mindstorms Kit and Development Environment.</td>
    </tr>
    <tr>
      <td>HTML5</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/120" target="_blank">Introduction to HTML5 - tag you’re it</a></td>
      <td>A Flash Card Like run through of the tags. (pun intended) The do’s, the dont’s, the will’s the wont’s.. the can’s and the can’t quite yets. We’ll take a brisk walk through this exciting progression into the next version of the Hypertext Markup Language. Come see what the fuss is all about, what you need to know now, and what you will want to keep your eye on for the future.</td>
    </tr>
    <tr>
      <td>Introduction to Android Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/158" target="_blank">Android Game Development with AndEngine</a></td>
      <td>Using AndEngine: https://www.andengine.org/ to develop a simple game for the Android platform</td>
    </tr>
    <tr>
      <td>Introduction to Android Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/159" target="_blank">RESTful Android</a></td>
      <td>Android Development with RESTful API’s</td>
    </tr>
    <tr>
      <td>Introduction to Scala</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/150" target="_blank">Introduction to Scala</a></td>
      <td>Scala is a general purpose programming language designed to express common programming patterns in a concise, elegant, and type-safe way. It smoothly integrates features of object-oriented and functional languages, enabling Java and other programmers to be more productive.</td>
    </tr>
    <tr>
      <td>Mobile</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/109" target="_blank">Creating a Windows Phone 7 App in Silverlight</a></td>
      <td>Creating a Windows Phone 7 App in Silverlight. Will discuss gotchas and workarounds.</td>
    </tr>
    <tr>
      <td>MS Dynamics CRM</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/114" target="_blank">CRM 2011 and OData</a></td>
      <td>Utilizing Silverlight to extend CRM 2011 using OData and REST dataservices</td>
    </tr>
    <tr>
      <td>Outside the Box</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/121" target="_blank">What is NoSQL?</a></td>
      <td>What is NoSQL? What are some of the NOSQL tools? What are the benefits? Real world examples.</td>
    </tr>
    <tr>
      <td>Ruby</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/99" target="_blank">Rails 101</a></td>
      <td>In less than an hour, we’ll build a simple Twitter clone complete with user accounts and tweets. We’ll cover models (which store your objects in a database), controllers (which retrieve and present model objects), and views (which render HTML to the browser).</td>
    </tr>
    <tr>
      <td>Ruby</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/100" target="_blank">Ruby-Processing Graphics Playground</a></td>
      <td>Ruby-Processing is a Ruby wrapper around Processing, a simple language that lets you create images and animations. We’ll demonstrate some of Processing’s basic features like drawing, manipulating image files, and interacting with the mouse. If the network cooperates, we’ll even run a server that will let attendees collaborate on an image!</td>
    </tr>
    <tr>
      <td>Ruby</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/153" target="_blank">Full Text Searching in Rails</a></td>
      <td>Learn how to install, configure, deploy and manage the Sphinx search engine and the ThinkingSphinx gem to bring simple, yet robust, full text searching to your Ruby on Rails application.</td>
    </tr>
    <tr>
      <td>Security</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/115" target="_blank">Source code review strategies</a></td>
      <td>Once you start reviewing source code for security issues, it because all to easy to get lost in the weeds. This presentation will outline many of the common practices used, and when to use them, to be successful.</td>
    </tr>
    <tr>
      <td>Security</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/117" target="_blank">Web Security - Beyond Lists</a></td>
      <td>Understanding what can happen– i.e vulnerability lists– is a great start in your journey. But what is more important is how you apply successful strategies– both in code AND in process– that prevent people from stealing your lunch money. This presentation will cover philosophies, principles, and strategies used to secure web applications beyond simplistic vulnerability lists. It will focus on things you could (and should) be doing right now and provide some direction on where you might consider going in the future.</td>
    </tr>
    <tr>
      <td>SharePoint</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/110" target="_blank">SharePoint 2010 as a Development Platform</a></td>
      <td>The goal of this session is to introduce attendees to the development of full-featured custom web applications using SharePoint 2010.</td>
    </tr>
    <tr>
      <td>Silverlight</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/108" target="_blank">MVVM using Caliburn.Micro</a></td>
      <td>Creating a Silverlight application using the MVVM pattern and Caliburn.Micro.</td>
    </tr>
    <tr>
      <td>Software Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/104" target="_blank">Project Management In Small Shops</a></td>
      <td>Describes how to scale down project management to small shops that don’t have a dedicated project manager. In small shops, every member of the team must be a project manager. In this session, we’ll talk about what works and what doesn’t. Tips and tricks for having pleased clients and happy developers! (This could make a good panel, if some other developers from small shops want to share their experiences.)</td>
    </tr>
    <tr>
      <td>Software Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/122" target="_blank">Using Regular Expressions</a></td>
      <td>Introduction to regular expressions? Using some basic Regex. Advanced Regex.</td>
    </tr>
    <tr>
      <td>Software Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/152" target="_blank">The Developer’s Guide To Scrum</a></td>
      <td>A hands on exploration of Scrum, the project management framework, focusing on the responsibilities and goals of the developer.</td>
    </tr>
    <tr>
      <td>Software Development</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/146" target="_blank">Lightswitch, what is it?</a></td>
      <td>Discussion of Lightswitch, where to get it, how to install it, what it can do</td>
    </tr>
    <tr>
      <td>Telecom</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/145" target="_blank">Tropo’s Phone Web Service API</a></td>
      <td>You will learn how to integrate Voice and SMS phone functionality into your existing web applications using Tropo.com’s RESTful Web API.</td>
    </tr>
    <tr>
      <td>WCF</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/160" target="_blank">Restful services with WCF</a></td>
      <td>Creating restful services via WCF and the WCF toolkit</td>
    </tr>
    <tr>
      <td>WCF</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/102" target="_blank">WCF 4.0: Simplifying the Developer Experience</a></td>
      <td>Windows Communication Foundation is the primary framework for building distributed applications and services. The major focus of WCF 4.0 was simplifying the learning curve and increasing adoption of this core communication technology in the Microsoft .Net stack. This session will explore new features and capabilities of WCF 4.0.</td>
    </tr>
    <tr>
      <td>WPF</td>
      <td><a href="https://nov2010.desertcodecamp.com/session/118" target="_blank">Get Declarative with WPF and Silverlight</a></td>
      <td>WPF and Silverlight applications are built using a mixture of XAML and code. In this presentation, we’ll look at techniques for reducing code by moving user interface logic into the XAML layer. We’ll look at attached behaviors, events, triggers, and using the Expression Blend SDK to extend the declarative power of XAML in WPF applications.</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Desert Code Camp" /><category term="Community" /><category term="Arizona" /><category term="Technology" /><summary type="html"><![CDATA[Here is the list of sessions that have been submitted and approved as of September 17th, 2010. It even includes 3 sessions for our Gangplank Jr. track for kids.]]></summary></entry><entry><title type="html">Announcing Desert Code Camp 2010.2</title><link href="https://www.josephguadagno.net/2010/08/17/announcing-desert-code-camp-2010-2/" rel="alternate" type="text/html" title="Announcing Desert Code Camp 2010.2" /><published>2010-08-16T20:47:00-07:00</published><updated>2010-08-16T20:47:00-07:00</updated><id>https://www.josephguadagno.net/2010/08/17/announcing-desert-code-camp-2010-2</id><content type="html" xml:base="https://www.josephguadagno.net/2010/08/17/announcing-desert-code-camp-2010-2/"><![CDATA[<p>I am pleased to announce the date for Desert Code Camp 2010.2, aka Nov2010 code camp.  It will take place <strong>Saturday, November, 13th, 2010</strong>. There are a few changes to this camp.</p>

<h2 id="new-web-site">New Web Site</h2>

<p>The website has been changed to accommodate history in the Desert Code Camps. This means that each Code Camp will have its own URL, so you can see what sessions have taken place in the past. This Desert Code Camp’s, 2010.2, URL is <a href="https://nov2010.desertcodecamp.com" target="_blank">https://nov2010.desertcodecamp.com</a> and is ready to accept sessions. May 2010 Desert Code Camp, 2010.1 Desert Code Camp, is <a href="https://may2010.desertcodecamp.com" target="_blank">https://may2010.desertcodecamp.com</a>. I am in the process of adding in the Desert Code Camps from 2009. You can go to <a href="https://desertcodecamp.com" target="_blank">https://desertcodecamp.com</a> to get the list of Code Camps that the site has.</p>

<h2 id="new-location">New Location</h2>

<p><a href="https://www2.cgc.maricopa.edu/" target="_blank">Chandler-Gilbert Community College</a> will be hosting the event on their <a href="https://www.cgc.maricopa.edu/adminservices/maps/pecos/Pages/PecosCampus.aspx" target="_blank">Pecos Campus</a>.</p>

<ul>
  <li>This facility is right off of the Loop 202 (Santan Freeway) and Gilbert Road at the intersection of Gilbert Rd and Pecos Rd, in Chandler, AZ.</li>
  <li>The facility provides plenty of free parking</li>
  <li>There is free wifi with no requirements or software installed on your pc.</li>
  <li>There is a student center for socializing and networking.</li>
  <li>Modern facilities. In fact, Desert Code Camp will most likely take place in their brand new <a href="https://www.cgc.maricopa.edu/adminservices/maps/pecos/Pages/pecos-i.aspx" target="_blank">Ironwood Hall</a> (IRN).</li>
  <li>Larger room sizes.  We will be able to hold several 120+ sessions.</li>
</ul>

<h2 id="speaker-dinner">Speaker Dinner</h2>

<p>I am going to try and have a speaker dinner also at this Desert Code Camp.  The speaker dinner will take place on Friday night, pending a sponsor. More information will be provided when we get closer. If you want to sponsor the speaker dinner for about 40 people, send me an email at jguadagno@hotmail.com.</p>

<h2 id="after-party">After Party</h2>

<p>We are working with the City of Chandler, and local businesses downtown to have several after parties going on in Downtown Chandler after the Code Camp.  Downtown Chandler is approximately 3 miles away from Chandler-Gilbert Community College. If you want to help, shoot me an email at jguadagno@hotmail.com.</p>

<h2 id="social-media">Social Media</h2>

<p>This Desert Code Camp we want you to use all of the Social Media avenues you can. We have the following:</p>

<ul>
  <li>A Twitter Account: <a href="https://twitter.com/desertcodecamp" target="_blank">@DesertCodeCamp</a></li>
  <li>A Facebook Group: <a href="https://www.facebook.com/group.php?gid=121127197936341&amp;v=app_2344061033&amp;ref=ts#!/group.php?gid=121127197936341&amp;ref=ts" target="_blank">Facebook group</a></li>
  <li>A Facebook Event: <a href="https://www.facebook.com/group.php?gid=121127197936341&amp;v=app_2344061033&amp;ref=ts#!/event.php?eid=144666038890143&amp;ref=mf" target="_blank">Facebook Event</a></li>
</ul>

<p><strong>Hope to see you there!</strong> Joseph Guadagno Camp Director</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Technology News" /><category term="Desert Code Camp" /><category term="Community" /><category term="Arizona" /><category term="Technology" /><category term="Events" /><summary type="html"><![CDATA[I am pleased to announce the date for Desert Code Camp 2010.2, aka Nov2010 code camp.  It will take place Saturday, November, 13th, 2010. There are a few changes to this camp.]]></summary></entry><entry><title type="html">Announcing PDCEvents.info</title><link href="https://www.josephguadagno.net/2010/08/05/announcing-pdcevents-info/" rel="alternate" type="text/html" title="Announcing PDCEvents.info" /><published>2010-08-05T14:16:05-07:00</published><updated>2010-08-05T14:16:05-07:00</updated><id>https://www.josephguadagno.net/2010/08/05/announcing-pdcevents-info</id><content type="html" xml:base="https://www.josephguadagno.net/2010/08/05/announcing-pdcevents-info/"><![CDATA[<p>Back a while ago, I created a site to track events at the Microsoft MVP Global Summit. I did this because there are so many after parties or events that happen at this summit as well as other conferences.  Well I put up a site to track events at the <a href="https://www.microsoftpdc.com" title="Microsoft PDC" target="_blank">Microsoft Professional Developers Conference</a> (PDC), it is <a href="https://bit.ly/PDCEvents" target="_blank">PDCEvents.info</a></p>

<p>Check it out. I have already added the events two events to the site. **<br />
Welcome Reception<strong>: <a href="https://bit.ly/pdc_1" target="_blank">https://bit.ly/pdc_1</a></strong><br />
Attendee Party**:<a href="https://bit.ly/pdc_2" target="_blank">https://bit.ly/pdc_2</a></p>

<p>The site has lots of feeds available, here are some links for the site:<br />
<strong>Home Page</strong>: <a href="https://bit.ly/PDCEvents" target="_blank">https://bit.ly/PDCEvents</a><br />
<strong>iCal Feed</strong>: <a href="https://bit.ly/PDCEventsCal" target="_blank">https://bit.ly/PDCEventsCal</a><br />
<strong>RSS Feed</strong>: <a href="https://bit.ly/PDCEventsRSS" target="_blank">https://bit.ly/PDCEventsRSS</a><br />
<strong>Maps Feed</strong>: <a href="https://bit.ly/PDCEventsMap" target="_blank">https://bit.ly/PDCEventsMap</a><br />
<strong>Mobile Site</strong>: <a href="https://bit.ly/PDCEventsMobile" target="_blank">https://bit.ly/PDCEventsMobile</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Technology News" /><category term="Community" /><category term="Events" /><category term="Microsoft" /><summary type="html"><![CDATA[Back a while ago, I created a site to track events at the Microsoft MVP Global Summit. I did this because there are so many after parties or events that happen at this summit as well as other conferences. Well I put up a site to track events at the Microsoft Professional Developers Conference (PDC), it is PDCEvents.info]]></summary></entry><entry><title type="html">Announcing PDCEvents.info</title><link href="https://www.josephguadagno.net/2010/07/14/announcing-pdcevents-info-2/" rel="alternate" type="text/html" title="Announcing PDCEvents.info" /><published>2010-07-13T21:56:52-07:00</published><updated>2010-07-13T21:56:52-07:00</updated><id>https://www.josephguadagno.net/2010/07/14/announcing-pdcevents-info-2</id><content type="html" xml:base="https://www.josephguadagno.net/2010/07/14/announcing-pdcevents-info-2/"><![CDATA[<p>One thing that I have noticed of the past few years when it comes to conference or “Summits” is that there are a lot of after parties, tweets, etc. that happen.  Most of the time it is difficult to get the word out about these events or even find out about them.  This sparked the creation of <a href="https://www.mvpsummitevents.com" target="_blank">MVPSummitEvents.com</a>. Now it is time for <a href="https://pdcevents.info" target="_blank">PDCEvents.info</a>. From the site you can add events, create venues, display events, subscribe to the RSS feed or iCal feed, download an event to outlook or calendar client that can consume a .VCS file. <strong>Note</strong>: the creation of events and venues requires you to sign in using your Windows Live Id account, no information from Live is used within our site.</p>

<p>Site: <a href="https://pdcevents.info" target="_blank">https://pdcevents.info</a></p>

<p>RSS: <a href="https://pdcevents.info/rss.ashx" title="https://pdcevents.info/rss.ashx" target="_blank">https://pdcevents.info/rss.ashx</a></p>

<p>iCal: <a href="https://pdcevents.info/calendar.ashx" title="https://pdcevents.info/calendar.ashx" target="_blank">https://pdcevents.info/calendar.ashx</a></p>

<p>GeoRSS: <a href="https://pdcevents.info/map.aspx" title="https://pdcevents.info/map.aspx" target="_blank">https://pdcevents.info/map.aspx</a></p>

<p>If you tag events with #PDC10, they will appear on <a href="https://pdcevents.info/Flickr.aspx" title="https://pdcevents.info/Flickr.aspx" target="_blank">https://pdcevents.info/Flickr.aspx</a></p>

<p>Let me know what you think at <a href="mailto:jguadagno@hotmail.com?subject=PDCEvents">jguadagno@hotmail.com</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Technology News" /><category term="Community" /><category term="Events" /><category term="Microsoft" /><summary type="html"><![CDATA[One thing that I have noticed of the past few years when it comes to conference or “Summits” is that there are a lot of after parties, tweets, etc. that happen.  Most of the time it is difficult to get the word out about these events or even find out about them.  This sparked the creation of MVPSummitEvents.com. Now it is time for PDCEvents.info. From the site you can add events, create venues, display events, subscribe to the RSS feed or iCal feed, download an event to outlook or calendar client that can consume a .VCS file. Note: the creation of events and venues requires you to sign in using your Windows Live Id account, no information from Live is used within our site.]]></summary></entry><entry><title type="html">Code Camp redone</title><link href="https://www.josephguadagno.net/2010/07/06/code-camp-redone/" rel="alternate" type="text/html" title="Code Camp redone" /><published>2010-07-06T10:16:44-07:00</published><updated>2010-07-06T10:16:44-07:00</updated><id>https://www.josephguadagno.net/2010/07/06/code-camp-redone</id><content type="html" xml:base="https://www.josephguadagno.net/2010/07/06/code-camp-redone/"><![CDATA[<p>So my family and I have been going camping a lot for the past two years and this got me thinking… Why not have a Code Camp with actual camping?  We can still learn, have fun and enjoy beautiful Arizona at the same time.</p>

<p>If you’re interested, help me with the planning.  Complete the survey at: <a href="https://bit.ly/bUnZvl">https://bit.ly/bUnZvl</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Desert Code Camp" /><category term="Community" /><category term="Arizona" /><summary type="html"><![CDATA[So my family and I have been going camping a lot for the past two years and this got me thinking… Why not have a Code Camp with actual camping? We can still learn, have fun and enjoy beautiful Arizona at the same time.]]></summary></entry><entry><title type="html">I Love Chandler…</title><link href="https://www.josephguadagno.net/2010/04/01/i-love-chandler/" rel="alternate" type="text/html" title="I Love Chandler…" /><published>2010-04-01T09:19:55-07:00</published><updated>2010-04-01T09:19:55-07:00</updated><id>https://www.josephguadagno.net/2010/04/01/i-love-chandler</id><content type="html" xml:base="https://www.josephguadagno.net/2010/04/01/i-love-chandler/"><![CDATA[<p>No not Chandler Bing from Friends, although maybe Monica :smile: … it’s living in Chandler, Arizona.</p>

<p>In the past, I have tried to keep this blog mostly technical but something happened this morning that made me realize I love living in <a href="https://www.chandleraz.gov" title="City of Chandler web site." target="_blank">Chandler, Arizona</a>. I forgot to close my garage door last night and during the <a href="https://chandlerpd.com" target="_blank">City of Chandler Police Departments</a> nightly patrol, the officer closed the door for me and left me a nice little note/reminder. Way to go!</p>

<p>A bit of history… My wife and I spend quite some time researching different cities and parts of the country when deciding where would be the best location (in our opinion) to raise our family. After all the research and talking to some friends and almost family out here we decided to move to Chandler, Arizona. So about 5 years ago in June of 2005, we moved from Long Island, NY and left all our family and friends to move to Chandler, Arizona.</p>

<p>So why do I love living in Chandler, Arizona? There are many reasons. The City of Chandler is proactively looking to grow the City in ways that most cities do not attempt.  The City of Chandler is hosting <a href="https://laidoffcampphoenix.com" target="_blank">Laid Off Camp Phoenix</a> on April 3rd, 2010.  They attempted to host <a href="https://desertcodecamp.com" target="_blank">Desert Code Camp</a> on May 15th, 2010 (just not enough time to plan) and they are hosting <a href="https://azgivecamp.org/Home.aspx" target="_blank">Arizona Give Camp</a> on May 21st-May 23rd, 2010.</p>

<p>The City of Chandler does so much for its citizens, Day of Fun and Fitness, Family Easter celebrations, Chandler Jazz festival,  and <a href="https://www.chandleraz.gov/newsreleasearchive.aspx" target="_blank">more</a>. The City of Chandler has won countless <a href="https://www.chandleraz.gov/default.aspx?pageid=22" target="_blank">awards</a> also.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Arizona" /><category term="Personal" /><summary type="html"><![CDATA[No not Chandler Bing from Friends, although maybe Monica :smile: … it’s living in Chandler, Arizona.]]></summary></entry><entry><title type="html">Announcing Desert Code Camp 2010.1</title><link href="https://www.josephguadagno.net/2010/03/29/announcing-desert-code-camp-2010-1/" rel="alternate" type="text/html" title="Announcing Desert Code Camp 2010.1" /><published>2010-03-29T13:57:49-07:00</published><updated>2010-03-29T13:57:49-07:00</updated><id>https://www.josephguadagno.net/2010/03/29/announcing-desert-code-camp-2010-1</id><content type="html" xml:base="https://www.josephguadagno.net/2010/03/29/announcing-desert-code-camp-2010-1/"><![CDATA[<p>It’s time for the 7th iteration of <a href="https://www.desertcodecamp.com" target="_blank">Desert Code Camp</a> (my second one as director).  Desert Code Camp will be held on May 15th at DeVry again.</p>

<p>DeVry University<br />
2149 W Dunlap Ave (<a href="https://www.desertcodecamp.com/Map.aspx" target="_blank">Map</a>)<br />
Phoenix, Arizona 85021</p>

<p>The Desert Code Camp website has a new design and logo thanks to <a href="https://gabrielutasi.com/" target="_blank">Gabriel Utasi</a> and a few new features.</p>

<p>Sign-up to give a talk today.</p>

<p>Follow Desert Code Camp on Twitter <a href="https://twitter.com/DesertCodeCamp" target="_blank">@DesertCodeCamp</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Desert Code Camp" /><category term="Arizona" /><category term="Technology" /><category term="Community" /><summary type="html"><![CDATA[It’s time for the 7th iteration of Desert Code Camp (my second one as director).  Desert Code Camp will be held on May 15th at DeVry again.]]></summary></entry><entry><title type="html">SEVDNUG: February Social Mixer</title><link href="https://www.josephguadagno.net/2010/02/11/sevdnug-february-social-mixer/" rel="alternate" type="text/html" title="SEVDNUG: February Social Mixer" /><published>2010-02-11T06:34:50-07:00</published><updated>2010-02-11T06:34:50-07:00</updated><id>https://www.josephguadagno.net/2010/02/11/sevdnug-february-social-mixer</id><content type="html" xml:base="https://www.josephguadagno.net/2010/02/11/sevdnug-february-social-mixer/"><![CDATA[<p>Join us for our 2nd social mixer of the year at Brunswick Zone in Gilbert for two hours of bowling and socializing taking place on Monday 2/22 at 6:00 PM.</p>

<p>We got a few lanes, a few shoes, some appetizers and some drinks sponsored by <a href="https://comsys.com/services/staffing.html" target="_blank">Comsys</a>*.</p>

<p><strong>Registration will be limited</strong>!</p>

<p>Register at: <a href="https://sevdnug0210.eventbrite.com" target="_blank">Event Registration</a></p>

<p class="notice--info">The sponsor will cover a certain amount of the cost.  You might be required to purchase some of your own food and drink.</p>

<p><a href="https://sevdnug0210.eventbrite.com" target="_blank">Event Registration</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Community" /><category term="User Group" /><category term=".NET" /><category term="Arizona" /><summary type="html"><![CDATA[Join us for our 2nd social mixer of the year at Brunswick Zone in Gilbert for two hours of bowling and socializing taking place on Monday 2/22 at 6:00 PM.]]></summary></entry><entry><title type="html">Two Free Developer Events in Phoenix</title><link href="https://www.josephguadagno.net/2010/02/11/two-free-developer-events-in-phoenix/" rel="alternate" type="text/html" title="Two Free Developer Events in Phoenix" /><published>2010-02-11T06:28:17-07:00</published><updated>2010-02-11T06:28:17-07:00</updated><id>https://www.josephguadagno.net/2010/02/11/two-free-developer-events-in-phoenix</id><content type="html" xml:base="https://www.josephguadagno.net/2010/02/11/two-free-developer-events-in-phoenix/"><![CDATA[<h2 id="msdn-events-take-your-applications-sky-high-with-cloud-computing-and-the-windows-azure-platform">MSDN Events: Take Your Applications Sky High with Cloud Computing and the Windows Azure Platform</h2>

<h3 id="february-23rd">February 23rd</h3>

<p>Join your local MSDN Events team as we take a deep dive into cloud computing and the Windows Azure Platform. We’ll start with a developer-focused overview of this new platform and the cloud computing services that can be used either together or independently to build highly scalable applications. As the day unfolds, we’ll explore data storage, SQL Azure, and the basics of deployment with Windows Azure. Register today for these free.</p>

<p>Register at: <a href="https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032438178&amp;Culture=en-US" target="_blank">https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032438178&amp;Culture=en-US</a></p>

<h2 id="msdn-events-presents-from-zero-to-xaml-in-4-hours">MSDN Events Presents: From Zero to XAML in 4 Hours</h2>

<h3 id="march-23rd">March 23rd</h3>

<p>Learning XAML for use in Windows Presentation Foundation (WPF) or Silverlight can be a little daunting. This seminar will introduce the basic concepts of XAML and show you how to get started quickly and easily. You will be given an overview of XAML tools such as <a href="https://VS.NET" target="_blank">VS.NET</a> 2010 and Expression Blend. If you are new to Silverlight and WPF you need to attend this seminar to see how to move to these powerful new platforms. The focus of this seminar is using XAML to build business applications.</p>

<p>Register at: <a href="https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032439056&amp;Culture=en-US" target="_blank">https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032439056&amp;Culture=en-US</a></p>

<p>These are free events.  I would register while there is still room!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Community" /><category term="User Group" /><category term=".NET" /><category term="Arizona" /><summary type="html"><![CDATA[MSDN Events: Take Your Applications Sky High with Cloud Computing and the Windows Azure Platform]]></summary></entry><entry><title type="html">Using Bing Maps Web Services Imagery Service</title><link href="https://www.josephguadagno.net/2010/02/04/using-bing-maps-web-services-imagery-service/" rel="alternate" type="text/html" title="Using Bing Maps Web Services Imagery Service" /><published>2010-02-04T11:28:51-07:00</published><updated>2010-02-04T11:28:51-07:00</updated><id>https://www.josephguadagno.net/2010/02/04/using-bing-maps-web-services-imagery-service</id><content type="html" xml:base="https://www.josephguadagno.net/2010/02/04/using-bing-maps-web-services-imagery-service/"><![CDATA[<p>In my previous post, <a href="/2010/02/04/using-the-bing-maps-web-services-for-geocoding-addresses/">Using the Bing Maps Web Services for Geocoding Addresses</a>, I talk about geocoding addresses using the Bing Maps Web Services. Now it is time to talk about getting imagery of maps, roads or aerials views for addresses or geocodes. In order to get started using the Bing Maps Web Services, check out the Getting Started section of <a href="/2010/02/04/using-the-bing-maps-web-services-for-geocoding-addresses/">Using the Bing Maps Web Services for Geocoding Addresses</a>.</p>

<h2 id="bing-maps-web-services">Bing Maps Web Services</h2>

<p>Bing Maps Web Services is a set of Web services that allow you to add mapping and search functionality to your application, including location finding, map imagery, and routing capabilities. For example, you can: Use the <a href="https://msdn.microsoft.com/en-us/library/cc981090.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Imagery Service</a> to:</p>

<ul>
  <li>Return a link to a map with a pushpin at a specific location</li>
  <li>Provide a road map or bird’s eye or aerial imagery to your application</li>
</ul>

<p>Use the <a href="https://msdn.microsoft.com/en-us/library/cc966826.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Route Service</a> to:</p>

<ul>
  <li>Get directions that include traffic warnings and route hints between multiple locations.</li>
  <li>Get directions from all major roads to a destination (1-click directions, also referred to as a “party map”) and then use the Imagery Service to map those routes.</li>
</ul>

<p>For this post, we will cover the Imagery service. Just like the Geocode Service, there is a request, <a href="https://msdn.microsoft.com/en-us/library/cc980912.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">MapUriRequest</a>, and response, <a href="https://msdn.microsoft.com/en-us/library/cc981042.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">MapUriResponse</a>, object for the Imagery Service. In order to get the Uri to display a map in your application, you will need to use the imagery service client, <a href="https://msdn.microsoft.com/en-us/library/cc980959.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ImageryServiceClient</a>. The ImageryServiceClient needs to be instantiated with the WCF endpoint to use, by default it should be <code class="language-plaintext highlighter-rouge">BasicHttpBinding_IImageryService</code>. Then call the <a href="https://msdn.microsoft.com/en-us/library/cc981108.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">GetMapUri</a> method passing your <a href="https://msdn.microsoft.com/en-us/library/cc980912.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">MapUriRequest</a> object.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">ImageryServiceClient</span> <span class="n">imageryService</span> <span class="p">=</span>
    <span class="k">new</span> <span class="nf">ImageryServiceClient</span><span class="p">(</span><span class="s">"BasicHttpBinding_IImageryService"</span><span class="p">);</span>
<span class="n">MapUriResponse</span> <span class="n">mapUriResponse</span> <span class="p">=</span> <span class="n">imageryService</span><span class="p">.</span><span class="nf">GetMapUri</span><span class="p">(</span><span class="n">mapUriRequest</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="building-the-mapurirequest">Building the MapUriRequest</h3>

<p>The MapUriRequest has two properties that need to be populated; the <a href="https://msdn.microsoft.com/en-us/library/cc966923.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Credentials</a> property which should contain you Bing Maps Id and either the <a href="https://msdn.microsoft.com/en-us/library/cc966747.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Center</a>, <a href="https://msdn.microsoft.com/en-us/library/cc966744.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">MajorRoutesDestination</a>, or <a href="https://msdn.microsoft.com/en-us/library/cc980872.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Pushpins</a> property.  The code snippet below demonstrates instantiating the MapUriRequest and setting the properties based on values passed into a method (outlined later).</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="n">MapUriRequest</span> <span class="n">mapUriRequest</span> <span class="p">=</span> <span class="k">new</span> <span class="n">MapUriRequest</span>
  <span class="p">{</span>
    <span class="n">Credentials</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Credentials</span> <span class="p">{</span><span class="n">ApplicationId</span> <span class="p">=</span> <span class="n">appId</span><span class="p">},</span>
    <span class="n">Pushpins</span> <span class="p">=</span> <span class="n">pushpins</span><span class="p">,</span>
    <span class="n">Center</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Location</span> <span class="p">{</span><span class="n">Latitude</span> <span class="p">=</span> <span class="n">latitude</span><span class="p">,</span> <span class="n">Longitude</span> <span class="p">=</span> <span class="n">longitude</span><span class="p">}</span>
  <span class="p">};</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now you can customize the options for the map using the <a href="https://msdn.microsoft.com/en-us/library/cc981074.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">MapUriOptions</a> property of the MapUriRequest object. Here is a list of the properties from the MSDN <a href="https://msdn.microsoft.com/en-us/library/cc981033.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">documentation</a>:</p>

<table>
  <thead>
    <tr>
      <th>Property name</th>
      <th>Description</th>
      <th>Default Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc981085.asp?WT.mc_id=DT-MVP-4024623x" target="_blank">DisplayLayers</a></td>
      <td>A <code class="language-plaintext highlighter-rouge">string</code> array indicating the layer data to display on the map.</td>
      <td><code class="language-plaintext highlighter-rouge">null</code></td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc966894.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ImageSize</a></td>
      <td>A <a href="https://msdn.microsoft.com/en-us/library/cc981005.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">SizeOfint Class</a> object specifying the height and width of the image to return.</td>
      <td>The default width is 350 and the default height is 350.</td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc980869.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ImageType</a></td>
      <td>An <a href="https://msdn.microsoft.com/en-us/library/cc966755.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ImageType Enumeration</a> value specifying the format of the image to return.</td>
      <td>The default value is <code class="language-plaintext highlighter-rouge">ImageType.Default</code>, which means the default changes depending on the map style specified.</td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc981009.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">PreventIconCollision</a></td>
      <td>A <code class="language-plaintext highlighter-rouge">bool</code> indicating whether or not to separate pushpin icons that are close to each other on the map so that they are more visible.</td>
      <td><code class="language-plaintext highlighter-rouge">false*</code></td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc966910.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Style</a></td>
      <td>A <a href="https://msdn.microsoft.com/en-us/library/cc966745.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">MapStyle Enumeration</a> value specifying the map style of the image to return.</td>
      <td><code class="language-plaintext highlighter-rouge">MapStyle.Road*</code></td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc981052.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">UriScheme</a></td>
      <td>A <a href="https://msdn.microsoft.com/en-us/library/cc981022.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">UriScheme Enumeration</a> value specifying the URI scheme to return.</td>
      <td><code class="language-plaintext highlighter-rouge">UriScheme.Http</code></td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc966900.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ZoomLevel</a></td>
      <td>An <code class="language-plaintext highlighter-rouge">int*</code> indicating the zoom level of the map to return.</td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>Assigning some of the options:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="c1">// Set the map options</span>
<span class="n">MapUriOptions</span> <span class="n">mapUriOptions</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">MapUriOptions</span><span class="p">();</span>
<span class="n">mapUriOptions</span><span class="p">.</span><span class="n">Style</span> <span class="p">=</span> <span class="n">MapStyle</span><span class="p">.</span><span class="n">Road</span><span class="p">;</span>
<span class="n">mapUriOptions</span><span class="p">.</span><span class="n">ZoomLevel</span> <span class="p">=</span> <span class="n">zoom</span><span class="p">;</span>
<span class="n">mapUriOptions</span><span class="p">.</span><span class="n">ImageSize</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SizeOfint</span> <span class="p">{</span><span class="n">Height</span> <span class="p">=</span> <span class="n">height</span><span class="p">,</span> <span class="n">Width</span> <span class="p">=</span> <span class="n">width</span><span class="p">};</span>
<span class="c1">// Set the options property of the request.</span>
<span class="n">mapUriRequest</span><span class="p">.</span><span class="n">Options</span> <span class="p">=</span> <span class="n">mapUriOptions</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Now you are ready to call the image service client to get the MapUriResponse.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">ImageryServiceClient</span> <span class="n">imageryService</span> <span class="p">=</span>
    <span class="k">new</span> <span class="nf">ImageryServiceClient</span><span class="p">(</span><span class="s">"BasicHttpBinding_IImageryService"</span><span class="p">);</span>
<span class="n">MapUriResponse</span> <span class="n">mapUriResponse</span> <span class="p">=</span> <span class="n">imageryService</span><span class="p">.</span><span class="nf">GetMapUri</span><span class="p">(</span><span class="n">mapUriRequest</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here is a helper class, <a href="/assets/downloads/Imagery.cs_.zip">Imagery.cs</a>,  which wraps the GetMapUri function with 8 different overloads.</p>

<h4 id="working-with-the-mapuriresponse">Working with the MapUriResponse</h4>

<p>The MapUriResponse object has three properties:</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/ee692183.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">BrandLogoUri</a></td>
      <td>The <code class="language-plaintext highlighter-rouge">System.Uri</code> of the Bing Maps brand logo image.</td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc980964.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ResponseSummary</a></td>
      <td>A <a href="https://msdn.microsoft.com/en-us/library/cc980902.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ResponseSummary Class</a> object describing the response that was returned by the service.</td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc980931.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Uri</a></td>
      <td>A <code class="language-plaintext highlighter-rouge">string</code> that is the URI of the requested map.</td>
    </tr>
  </tbody>
</table>

<p>For brevity sake, we will just use the <code class="language-plaintext highlighter-rouge">Uri</code> property.  You should, though, for good programming practices, check the ResponseSummary property for any exceptions.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="kt">string</span> <span class="n">mapUri</span> <span class="p">=</span> <span class="n">Imagery</span><span class="p">.</span><span class="nf">GetMapUri</span><span class="p">(</span><span class="s">"YourAppId"</span><span class="p">,</span> <span class="m">47.62</span><span class="p">,</span> <span class="p">-</span><span class="m">122.2</span><span class="p">);</span>
<span class="n">imgMap</span><span class="p">.</span><span class="n">imageUrl</span> <span class="p">=</span> <span class="n">mapUri</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><img src="/assets/images/posts/image_thumb.png" alt="image-right" title="downtown Bellevue" class="align-right" /></p>

<p>This call retrieves the Uri to use to display a 200x200 road map of the area at latitude 47.62 and longitude -122.2 with a zoom of 14, which is downtown Bellevue, WA. If you want to add pushpins or markers similar to the above image you will need to populate the array of <a href="https://msdn.microsoft.com/en-us/library/cc966869.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">PushPin</a> objects. A PushPin object has an <a href="https://msdn.microsoft.com/en-us/library/cc980903.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">IconStyle</a> which is the type of icon to use, a <a href="https://msdn.microsoft.com/en-us/library/cc981045.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Label</a> which an optional text to display on the pushpin (only works with certain pushpins) and the <a href="https://msdn.microsoft.com/en-us/library/cc966941.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Location</a> which contains the latitude and longitude that the pushpin should be located at. That’s it.  It seems like a lot of work for a one-line call.  With the attached <a href="/assets/downloads/Imagery.cs_.zip">Imagery.cs</a> class, a lot of the overhead work was done for you.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Bing" /><category term="Web" /><summary type="html"><![CDATA[In my previous post, Using the Bing Maps Web Services for Geocoding Addresses, I talk about geocoding addresses using the Bing Maps Web Services. Now it is time to talk about getting imagery of maps, roads or aerials views for addresses or geocodes. In order to get started using the Bing Maps Web Services, check out the Getting Started section of Using the Bing Maps Web Services for Geocoding Addresses.]]></summary></entry><entry><title type="html">Using the Bing Maps Web Services for Geocoding Addresses</title><link href="https://www.josephguadagno.net/2010/02/04/using-the-bing-maps-web-services-for-geocoding-addresses/" rel="alternate" type="text/html" title="Using the Bing Maps Web Services for Geocoding Addresses" /><published>2010-02-04T09:06:53-07:00</published><updated>2010-02-04T09:06:53-07:00</updated><id>https://www.josephguadagno.net/2010/02/04/using-the-bing-maps-web-services-for-geocoding-addresses</id><content type="html" xml:base="https://www.josephguadagno.net/2010/02/04/using-the-bing-maps-web-services-for-geocoding-addresses/"><![CDATA[<p>For the <a href="https://www.mvpsummitevents.com/" target="_blank">MVPSummitEvents</a> and <a href="https://www.visitmixevents.info" target="_blank">Mix10Events</a> site, I wanted to create a map of all of the events listed on the site. In order to do that I needed to <a href="https://en.wikipedia.org/wiki/Geocoding" target="_blank">Geocode</a> all of the addresses for the events.  There are several services out there for geocoding an address, <a href="https://msdn.microsoft.com/en-us/library/cc966793.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Microsoft</a>, <a href="https://developer.yahoo.com/maps/rest/V1/geocode.html" target="_blank">Yahoo</a>, and <a href="https://code.google.com/apis/maps/documentation/geocoding/" target="_blank">Google</a> provide this service as well as others.  I decided to go with the Microsoft Bing services, being a <a href="https://mvp.support.microsoft.com/profile=4C0083AE-C0DE-4F05-A179-D9072AF2EA2B" target="_blank">Microsoft MVP</a>.</p>

<h3 id="getting-started">Getting Started</h3>

<p>Let’s get started. MSDN has just about everything you need to <a href="https://msdn.microsoft.com/en-us/library/cc966926.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">get started</a> with using the Bing Map Web Services. Step 1: The first step is to get a key or token to use in your application for the Bing Maps Web Services application. This can be done by visiting the <a href="https://www.bingmapsportal.com" target="_blank">Bing Maps Account center</a> and clicking on <strong>Create a Bing Maps account</strong>. Step 2: If you are using Visual Studio, add a service reference to one or more Bing Maps Web Services that provide the features you need. See the <a href="https://msdn.microsoft.com/en-us/library/cc980833.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Generating Client Proxy Classes</a> topic and the <a href="https://msdn.microsoft.com/en-us/library/cc966738.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Bing Maps Web Services Metadata</a> topic.</p>

<p><a href="/assets/images/posts/VirtualEarthWebServices.png"><img src="/assets/images/posts/VirtualEarthWebServices_thumb.png" alt="VirtualEarthWebServices" title="VirtualEarthWebServices" /></a></p>

<p>Whether you used Visual Studio or the <code class="language-plaintext highlighter-rouge">svcutil</code> application you should have one file, most likely named VirtualEarthWebServices.cs. The file will contain a bunch of wrapper classes around the Bing Maps Web Services, and the required Windows Communication Foundation (WCF) classes. You will also see the generated configuration settings for the app or web config files. Step 3: Set every Bing Maps Web Services request a valid Credentials property. You will see more on this in a bit.</p>

<h3 id="geocoding-an-address">Geocoding an Address</h3>

<p>There are two properties that are required to successfully request a GeoCode for an address.</p>

<ol>
  <li>Set the <a href="https://msdn.microsoft.com/en-us/library/cc966923.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Credential</a> Property of the <a href="https://msdn.microsoft.com/en-us/library/cc980924.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">GeoCodeRequest</a> object</li>
  <li>Set either the <a href="https://msdn.microsoft.com/en-us/library/cc981130.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Query</a> property or <a href="https://msdn.microsoft.com/en-us/library/cc966788.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Address</a> property of the <code class="language-plaintext highlighter-rouge">GeoCodeRequest</code> object.</li>
</ol>

<p>Here is a helper function that wraps the call to GeoCodeRequest. This method will return a <a href="https://msdn.microsoft.com/en-us/library/cc980928.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">GeocodeResponse</a> object. The <code class="language-plaintext highlighter-rouge">GeocodeResponse</code> object contains three properties that are populated based on the query.</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/ee692183.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">BrandLogoUri</a></td>
      <td>The <code class="language-plaintext highlighter-rouge">System.Uri</code> of the Bing Maps brand logo image. (Inherited from the <a href="https://msdn.microsoft.com/en-us/library/cc981076.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ResponseBase Class</a>.)</td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc980964.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ResponseSummary</a></td>
      <td>A <a href="https://msdn.microsoft.com/en-us/library/cc980902.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ResponseSummary Class</a> object describing the response that was returned by the service. (Inherited from the <a href="https://msdn.microsoft.com/en-us/library/cc981076.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">ResponseBase Class</a>.) This class returns any exceptions that we raised during the request.</td>
    </tr>
    <tr>
      <td><a href="https://msdn.microsoft.com/en-us/library/cc980800.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Results</a></td>
      <td>A <a href="https://msdn.microsoft.com/en-us/library/cc980950.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">GeocodeResult Class</a> array, where each element is a possible match returned by the Geocode Service.</td>
    </tr>
  </tbody>
</table>

<p>To keep this article short(er) I will just cover the <code class="language-plaintext highlighter-rouge">Results</code> object. Depending on the Confidence filter and Geocode options that were set in the call you could receive more than one result.</p>

<p>Let’s assume that we only want to work with the first result and get the Geocode for “1 Microsoft Way, Redmond, WA”. We simply call the static method of <code class="language-plaintext highlighter-rouge">GetGeocodeResponse</code> and pass in the Bing Maps API key and the address to search for.</p>

<p>Assuming the address was found we can now work with the properties of the GeocodeResult class to find out the Geocode.  The Geocode is located in the <a href="https://msdn.microsoft.com/en-us/library/cc966919.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Locations</a> property which is an array of <a href="https://msdn.microsoft.com/en-us/library/cc966778.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">GeocodeLocation</a> objects. If the Count of the Locations is greater than one, let’s just take the first one and update the txtLatitude and txtLongitude objects.</p>

<p>That’s it. Next up, using the Bing Maps Web Services for getting map images.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Bing" /><category term="Bing Maps" /><category term="Web" /><summary type="html"><![CDATA[For the MVPSummitEvents and Mix10Events site, I wanted to create a map of all of the events listed on the site. In order to do that I needed to Geocode all of the addresses for the events. There are several services out there for geocoding an address, Microsoft, Yahoo, and Google provide this service as well as others. I decided to go with the Microsoft Bing services, being a Microsoft MVP.]]></summary></entry><entry><title type="html">Mix 10 Events Site Launched</title><link href="https://www.josephguadagno.net/2010/01/28/mix-10-events-site-launched/" rel="alternate" type="text/html" title="Mix 10 Events Site Launched" /><published>2010-01-28T07:25:17-07:00</published><updated>2010-01-28T07:25:17-07:00</updated><id>https://www.josephguadagno.net/2010/01/28/mix-10-events-site-launched</id><content type="html" xml:base="https://www.josephguadagno.net/2010/01/28/mix-10-events-site-launched/"><![CDATA[<p>Are you going to <a href="https://live.visitmix.com" title="Mix10" target="_blank">Mix10</a> in March? If so, you might want to check out the new site I launched call <a href="https://visitmixevents.info" title="VisitMixEvents.info" target="_blank">Mix 10 Events</a>.  Mix 10 Events will be you one stop shop for all of the events and after parties at Mix10. Anyone can create an event on the sign. All you need to do is sign in with you Windows Live credentials, and link to create an event will appear.  Please note, the event will not be available until it is reviewed and approved for display. <img src="/assets/images/posts/visitmixinfo-snapshot.png" alt="VisitMixEvents.info" title="VisitMixEvents.info" target="_blank" /></p>

<h3 id="staying-connected">Staying connected</h3>

<p>Don’t feel like checking out the site every day to see what is happening? Here are a few resources to help you.</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>The site</td>
      <td><a href="https://www.visitmixevents.info/default.aspx" title="https://www.visitmixevents.info/default.aspx" target="_blank">https://www.visitmixevents.info/default.aspx</a></td>
    </tr>
    <tr>
      <td>RSS Feed</td>
      <td><a href="https://www.visitmixevents.info/rss.ashx" title="https://www.visitmixevents.info/rss.ashx" target="_blank">https://www.visitmixevents.info/rss.ashx</a></td>
    </tr>
    <tr>
      <td>GeoRSS Feed</td>
      <td><a href="https://www.visitmixevents.info/rss.ashx?format=geo" title="https://www.visitmixevents.info/rss.ashx?format=geo" target="_blank">https://www.visitmixevents.info/rss.ashx?format=geo</a></td>
    </tr>
    <tr>
      <td>iCal Feed</td>
      <td><a href="https://www.visitmixevents.info/calendar.ashx" title="https://www.visitmixevents.info/calendar.ashx" target="_blank">https://www.visitmixevents.info/calendar.ashx</a></td>
    </tr>
    <tr>
      <td>Map of events</td>
      <td><a href="https://www.visitmixevents.info/map.aspx" title="https://www.visitmixevents.info/map.aspx" target="_blank">https://www.visitmixevents.info/map.aspx</a></td>
    </tr>
  </tbody>
</table>

<h3 id="mobile-users">Mobile Users</h3>

<p>You will be able to view all of the events that are happening, even filter by day. In addition, you can view the events on a Map, and check out the event info.</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Mobile Site</td>
      <td><a href="https://www.visitmixevents.info/m/default.aspx" target="_blank">https://www.visitmixevents.info/m/default.aspx</a></td>
    </tr>
    <tr>
      <td>Mobile Map</td>
      <td><a href="https://www.visitmixevents.info/m/m.aspx" target="_blank">https://www.visitmixevents.info/m/m.aspx</a></td>
    </tr>
  </tbody>
</table>

<h3 id="las-vegas">Las Vegas</h3>

<p>In case you do not want to check out the events, I mean “It’s Vegas Baby”. Here are some other resources, also accessible from the site.</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Bing Local</td>
      <td><a href="https://www.bing.com/local/default.aspx?q=Las+Vegas%2C+Nevada" title="https://www.bing.com/local/default.aspx?q=Las+Vegas%2C+Nevada" target="_blank">https://www.bing.com/local/default.aspx?q=Las+Vegas%2C+Nevada</a></td>
    </tr>
    <tr>
      <td>Official Las Vegas Tourism</td>
      <td><a href="https://www.visitlasvegas.com/vegas/index.jsp" title="https://www.visitlasvegas.com/vegas/index.jsp" target="_blank">https://www.visitlasvegas.com/vegas/index.jsp</a></td>
    </tr>
    <tr>
      <td>Vegas.com Guide</td>
      <td><a href="https://www.vegas.com/traveltips/" title="https://www.vegas.com/traveltips/" target="_blank">https://www.vegas.com/traveltips/</a></td>
    </tr>
  </tbody>
</table>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Mix10" /><category term="Events" /><category term="Community" /><category term="Microsoft" /><summary type="html"><![CDATA[Are you going to Mix10 in March? If so, you might want to check out the new site I launched call Mix 10 Events.  Mix 10 Events will be you one stop shop for all of the events and after parties at Mix10. Anyone can create an event on the sign. All you need to do is sign in with you Windows Live credentials, and link to create an event will appear.  Please note, the event will not be available until it is reviewed and approved for display.]]></summary></entry><entry><title type="html">MVP Summit Events Launched</title><link href="https://www.josephguadagno.net/2009/12/23/mvp-summit-events-launched/" rel="alternate" type="text/html" title="MVP Summit Events Launched" /><published>2009-12-23T14:20:14-07:00</published><updated>2009-12-23T14:20:14-07:00</updated><id>https://www.josephguadagno.net/2009/12/23/mvp-summit-events-launched</id><content type="html" xml:base="https://www.josephguadagno.net/2009/12/23/mvp-summit-events-launched/"><![CDATA[<p>One thing that I have noticed of the past few years when it comes to conference or “Summits” is that there are a lot of after parties, tweets, etc. that happen.  Most of the time it is difficult to get the word out about these events or even find out about them.  That’s where <a href="https://www.mvpsummitevents.com" target="_blank">MVPSummitEvents.com</a> comes in.  The site will hopefully be a one-stop shop to find <strong>public</strong> events that are happening at the Microsoft 2010 MVP Global Summit.</p>

<p>From the site, you can add events, create venues, display events, subscribe to the RSS feed or iCal feed, download an event to outlook or calendar client that can consume a VCS file. <strong>Note</strong>: the creation of events and venues requires you to sign in using your Windows Live Id account, no information from Live is used within our site.</p>

<p>If the site is successful, I plan to white label it to use with other events like MIX2010, TechEd, PDC, etc.</p>

<p>Let me know what you think at <a href="mailto:jguadagno@hotmail.com">jguadagno@hotmail.com</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="News" /><category term="MVP Summit" /><category term="Events" /><category term="Community" /><category term="Microsoft" /><summary type="html"><![CDATA[One thing that I have noticed of the past few years when it comes to conference or “Summits” is that there are a lot of after parties, tweets, etc. that happen. Most of the time it is difficult to get the word out about these events or even find out about them. That’s where MVPSummitEvents.com comes in. The site will hopefully be a one-stop shop to find public events that are happening at the Microsoft 2010 MVP Global Summit.]]></summary></entry><entry><title type="html">Call for speakers for 2010</title><link href="https://www.josephguadagno.net/2009/12/04/call-for-speakers-for-2010" rel="alternate" type="text/html" title="Call for speakers for 2010" /><published>2009-12-04T07:51:15-07:00</published><updated>2009-12-04T07:51:15-07:00</updated><id>https://www.josephguadagno.net/2009/12/04/call-for-speakers-for-2010</id><content type="html" xml:base="https://www.josephguadagno.net/2009/12/04/call-for-speakers-for-2010"><![CDATA[<p>The <a href="https://www.sevdnug.org" target="_blank">Southeast Valley .NET User Group</a> is looking for some speakers for 2010. The meetings are held monthly typically on the fourth Thursday of the month, although we can move the meeting to make it easier for speakers.  The meetings are held in Chandler, AZ about 20 miles south of downtown Phoenix, AZ.</p>

<p>This year we had Tim Rayburn and Alan Stevens speak at the user group.  Add your name to the list, contact me at <a href="mailto:jguadagno@hotmail.com">jguadagno@hotmail.com</a>.</p>

<p>Here are some of the topics that the community is looking for</p>

<ul>
  <li>ASP.NET</li>
  <li>Entity Framework</li>
  <li>LINQ</li>
  <li>Managed Extensibility Framework (MEF)</li>
  <li>Windows Communication Framework</li>
  <li>Windows Presentation Framework</li>
</ul>

<p>I hope to hear from you!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Community" /><category term="User Group" /><category term=".NET" /><summary type="html"><![CDATA[The Southeast Valley .NET User Group is looking for some speakers for 2010. The meetings are held monthly typically on the fourth Thursday of the month, although we can move the meeting to make it easier for speakers. The meetings are held in Chandler, AZ about 20 miles south of downtown Phoenix, AZ.]]></summary></entry><entry><title type="html">Events for 2010</title><link href="https://www.josephguadagno.net/2009/12/04/events-for-2010/" rel="alternate" type="text/html" title="Events for 2010" /><published>2009-12-04T07:36:16-07:00</published><updated>2009-12-04T07:36:16-07:00</updated><id>https://www.josephguadagno.net/2009/12/04/events-for-2010</id><content type="html" xml:base="https://www.josephguadagno.net/2009/12/04/events-for-2010/"><![CDATA[<p>I recently sat down with some other community evangelists in the Chandler / Phoenix area to talk about some of the events that I plan to put on in the coming year.</p>

<p>To get updates on these events, follow me on <a href="https://www.twitter.com" target="_blank">Twitter</a> <a href="https://www.twitter.com/jguadagno" target="_blank">@jguadagno</a> and the Southeast Valley .NET User Group <a href="https://www.twitter.com/sevdnug" target="_blank">@SEVDNUG</a> and continue to watch this blog.</p>

<p class="notice">Putting on an event is time-consuming, any help I receive is appreciated.  If you are interested let me know… Email me: <a href="mailto:jguadagno@hotmail.com">jguadagno@hotmail.com</a></p>

<p>Here is the list, please note that the dates are not set in stone yet.</p>

<h2 id="weekly-events">Weekly Events</h2>

<h3 id="tech-lunch-south">Tech Lunch South</h3>

<p>A weekly technology agnostic lunch meeting open to all.</p>

<p><a href="https://www.techlunchsouth.com" target="_blank">https://www.techlunchsouth.com</a></p>

<p>Every Tuesday.</p>

<h3 id="hacknight">Hacknight</h3>

<p>A weekly technology agnostic event open to all.</p>

<p><a href="https://gangplankhq.com/collaborative/" title="https://gangplankhq.com/collaborative/" target="_blank">https://gangplankhq.com/collaborative/</a></p>

<p>Every Wednesday night.</p>

<h2 id="monthly-events">Monthly Events</h2>

<h3 id="southeast-valley-net-user-group">Southeast Valley .NET User Group</h3>

<p><a href="https://www.sevdnug.org" target="_blank">https://www.sevdnug.org</a></p>

<p>Monthly on the 4th Thursday of the month.</p>

<h3 id="gangplank-jr">Gangplank Jr.</h3>

<p>A monthly meeting dedicated to kids from 4 – 18 years of age. Topics include, development, robotics, drawing, photography, cooking and more.</p>

<p><a href="https://www.gankplankjr.com" target="_blank">https://www.gankplankjr.com</a></p>

<p>Monthly (the day has not been determined yet)</p>

<h2 id="the-rest-of-year">The Rest of Year</h2>

<h3 id="phoenix-bar-camp">Phoenix Bar Camp</h3>

<p><a href="https://www.barcamp.org/BarCamp" target="_blank">BarCamp</a> is an ad-hoc gathering born from the desire for people to share and learn in an open environment. It is an intense event with discussions, demos, and interaction from participants who are the main actors of the event.</p>

<p><a href="https://www.barcampphoenix.com" target="_blank">https://www.barcampphoenix.com</a></p>

<p>February and August</p>

<h3 id="desert-code-camp">Desert Code Camp</h3>

<p><a href="https://www.desertcodecamp.com" target="_blank">https://www.desertcodecamp.com</a></p>

<p>May and November</p>

<h3 id="azgivecamp-">AZGiveCamp *</h3>

<p>AZGiveCamp is a massive weekend event where the software development community comes together to support charities and non-profits by developing or improving their websites and applications. It’s fun, it’s agile, it’s geeky, and it’s good for the community.</p>

<p>If you are interested in helping out as a volunteer, organizer, or sponsor, we would like to hear from you! Please check out our FAQs, and then drop them an email through our <a href="https://azgivecamp.giving.officelive.com/contactus.aspx" target="_blank">Contact Us</a> page.</p>

<p><a href="https://www.azgivecamp.org" target="_blank">https://www.azgivecamp.org</a></p>

<p>March or April.</p>

<h3 id="gangplank-jr-code-camp">Gangplank Jr. Code Camp</h3>

<p>A code camp style event for kids from the ages of 4 – 18.</p>

<p><em>no URL yet</em></p>

<p>Quarterly or semi-annual</p>

<h3 id="startup-weekend-">Startup Weekend **</h3>

<p><a href="https://phoenix.startupweekend.org" target="_blank">https://phoenix.startupweekend.org</a></p>

<p>Spring</p>

<p><strong>Legend:</strong></p>

<p>(*) indicates an event that is taking place in the area that I am not running but I am part of.</p>

<p>(**) this event might need someone to organize it the area again.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Community" /><category term="User Group" /><category term=".NET" /><summary type="html"><![CDATA[I recently sat down with some other community evangelists in the Chandler / Phoenix area to talk about some of the events that I plan to put on in the coming year.]]></summary></entry><entry><title type="html">A November to Remember</title><link href="https://www.josephguadagno.net/2009/11/09/a-november-to-remember/" rel="alternate" type="text/html" title="A November to Remember" /><published>2009-11-09T06:59:00-07:00</published><updated>2009-11-09T06:59:00-07:00</updated><id>https://www.josephguadagno.net/2009/11/09/a-november-to-remember</id><content type="html" xml:base="https://www.josephguadagno.net/2009/11/09/a-november-to-remember/"><![CDATA[<p>And no it is not because the Yankees won the World Series (although congrats to them) it is because it has been an awesome month for me personally and professionally.</p>

<h2 id="on-the-personal-front">On the personal front…</h2>

<p>My son’s baseball team, which I am an assistant coach, finished the season undefeated in regular season games (end of year tournament in next week).  My son hit his first over the fence home run which was a Grand Slam (technically not November but pretty awesome). My daughter’s softball team, which I am an assistant coach, finished the season undefeated in regular season games (end of year tournament in next week).</p>

<p>I get to go fly back to NY in a few days to see family and friends and wish my mother a happy 60th birthday.</p>

<h2 id="on-the-professional-front">On the professional front…</h2>

<p>I was elected to the North American <a href="https://www.ineta.org" target="_blank">INETA</a> board of directors.</p>

<p>I was elected to the <a href="https://www.azgivecamp.org" target="_blank">AZGiveCamp</a> executive committee, VP of Operations.  As the VP I will oversee the activities of the Logistics, Sponsorship, Technology and Publicity teams.</p>

<p>I put together my first Code Camp, <a href="https://www.desertcodecamp.com/" target="_blank">Desert Code Camp</a>.</p>

<p>I am presenting a <a href="https://pdcbof.com" target="_blank">Bird of Feather</a> session at PDC with Chris Woodruff on “How to Build and Enrich Your Technical and Local Community” on Tuesday, Nov 17 from 11a – 12p.  Stop by if you want to see how to build your community.</p>

<p>That’s it for now.  Hopefully, I can relax a bit in December.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="News" /><category term="Community" /><category term="INETA" /><category term="Desert Code Camp" /><category term="Personal" /><summary type="html"><![CDATA[And no it is not because the Yankees won the World Series (although congrats to them) it is because it has been an awesome month for me personally and professionally.]]></summary></entry><entry><title type="html">Desert Code Camp – November 2009 Lessons Learned</title><link href="https://www.josephguadagno.net/2009/11/09/desert-code-camp-november-2009-lessons-learned/" rel="alternate" type="text/html" title="Desert Code Camp – November 2009 Lessons Learned" /><published>2009-11-09T06:32:37-07:00</published><updated>2009-11-09T06:32:37-07:00</updated><id>https://www.josephguadagno.net/2009/11/09/desert-code-camp-november-2009-lessons-learned</id><content type="html" xml:base="https://www.josephguadagno.net/2009/11/09/desert-code-camp-november-2009-lessons-learned/"><![CDATA[<p>My first Code Camp is done and I wanted to jot down some of the lessons I learned.</p>

<h2 id="bring-extra-equipment">Bring EXTRA equipment!</h2>

<p>You will never now what people will want or need. Here’s a list of items that are good to have.</p>

<ul>
  <li>Adapters
    <ul>
      <li>DVI to VGA (those MACs)</li>
      <li>HDMI to VGA</li>
      <li>Multiple video to VGA</li>
    </ul>
  </li>
  <li>Mice</li>
  <li>CAT5 cables</li>
  <li>USB Cables</li>
  <li>USB Flash drive</li>
</ul>

<h2 id="speakers">Speakers</h2>

<ul>
  <li>Speakers will cancel.  It is an unfortunate fact.</li>
  <li>Don’t allow one speaker to speak at more than 3 sessions.  This reduces your risk in case of cancellation and prevents speaker fatigue.</li>
</ul>

<h2 id="schedule">Schedule</h2>

<p>Printed schedules are good to have and good to hang.  Also add speaker names to the presentations.</p>

<h2 id="name-tags">Name Tags</h2>

<p>People like and want them.</p>

<h2 id="venue">Venue</h2>

<ul>
  <li>WIFI is key.  A reliable and unobtrusive WiFi connection is best.</li>
  <li>You need to be able handle last minute room changes</li>
</ul>

<h2 id="food">Food</h2>

<ul>
  <li>Make sure you provide the person(s) that are ordering the food explicit instructions on the food.</li>
  <li>Try to have more than just pizza.</li>
  <li>You should have vegetarian and vegan options.</li>
</ul>

<p>And remember <strong>you are not going to be able to make <em>EVERYONE</em> happy.</strong></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Desert Code Camp" /><category term="Arizona" /><category term="Community" /><summary type="html"><![CDATA[My first Code Camp is done and I wanted to jot down some of the lessons I learned.]]></summary></entry><entry><title type="html">Gangplank Jr Track at Code Camp Jr.</title><link href="https://www.josephguadagno.net/2009/10/19/gangplank-jr-track-at-code-camp-jr/" rel="alternate" type="text/html" title="Gangplank Jr Track at Code Camp Jr." /><published>2009-10-19T14:35:29-07:00</published><updated>2009-10-19T14:35:29-07:00</updated><id>https://www.josephguadagno.net/2009/10/19/gangplank-jr-track-at-code-camp-jr</id><content type="html" xml:base="https://www.josephguadagno.net/2009/10/19/gangplank-jr-track-at-code-camp-jr/"><![CDATA[<p>Got a kid interested in Technology? Bring them to the <strong>Gankplank Jr track at Code Camp Jr.</strong> taking place at <a href="https://www.desertcodecamp.com/" target="_blank">Desert Code Camp</a>.</p>

<h2 id="schedule-for-the-day">Schedule for the day</h2>

<table>
  <thead>
    <tr>
      <th>Time</th>
      <th>Event</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>9:00</td>
      <td>Registration</td>
    </tr>
    <tr>
      <td>9:30</td>
      <td>Programming with Scratch</td>
    </tr>
    <tr>
      <td>9:45</td>
      <td>Free time with Scratch</td>
    </tr>
    <tr>
      <td>10:15</td>
      <td>Kid free play time</td>
    </tr>
    <tr>
      <td>10:45</td>
      <td>Lego Mindstorm NXT</td>
    </tr>
    <tr>
      <td>11:30</td>
      <td>Lego Mindstorm NXT free play time</td>
    </tr>
    <tr>
      <td>12:00</td>
      <td>Lunch provided by Gangplank Jr.</td>
    </tr>
    <tr>
      <td>Bakugan Tournament</td>
      <td> </td>
    </tr>
    <tr>
      <td>1:00</td>
      <td>Circuits</td>
    </tr>
    <tr>
      <td>1:45</td>
      <td>Free time with circuits and electronics</td>
    </tr>
    <tr>
      <td>3:00</td>
      <td>Wrap up</td>
    </tr>
    <tr>
      <td>3:15</td>
      <td>Free time with the computers</td>
    </tr>
  </tbody>
</table>

<p>Computers will be provided, feel free to bring a laptop if you have.</p>

<p>Lunch will be provided by Gangplank Jr.</p>

<p>If you have any Arduino sets or Lego Mindstorm NXT sets feel free to bring them.</p>

<p>Register to attend: <a href="https://codecampjr2009.eventbrite.com">codecampjr2009.eventbrite.com</a></p>

<p>If you have any questions, feel free to contact me.</p>

<p>Joseph Guadagno at <a href="mailto:jguadagno@hotmail.com">jguadagno@hotmail.com</a></p>

<p>Recommended for kids from 4-18 years of age.</p>

<p class="notice">It is recommended that the kids are at reading age.</p>

<p class="notice--alert">Schedule is subject to change.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Desert Code Camp" /><category term="Arizona" /><category term="Community" /><summary type="html"><![CDATA[Got a kid interested in Technology? Bring them to the Gankplank Jr track at Code Camp Jr. taking place at Desert Code Camp.]]></summary></entry><entry><title type="html">Another Phoenix area meet up</title><link href="https://www.josephguadagno.net/2009/10/01/another-phoenix-area-meet-up/" rel="alternate" type="text/html" title="Another Phoenix area meet up" /><published>2009-10-01T09:01:50-07:00</published><updated>2009-10-01T09:01:50-07:00</updated><id>https://www.josephguadagno.net/2009/10/01/another-phoenix-area-meet-up</id><content type="html" xml:base="https://www.josephguadagno.net/2009/10/01/another-phoenix-area-meet-up/"><![CDATA[<p>Phoenix Geek Tag was a minor success so I decided to put together another Geek outing in October. Most likely October 20th starting around 6pm. I have narrowed it down to two places.</p>

<p><strong>Option 1</strong>: Bowling in Gilbert at <a href="https://www.bowlbrunswick.com/home/home-center?op=setScratch;scratchName=centerNum;scratchValue=1090" target="_blank">Brunswick Lanes</a></p>

<p>What’s included…</p>

<ul>
  <li>Bowling with bowling shoes for two hours.</li>
  <li>Drinks (alcoholic and non) are <strong>not included</strong>.</li>
  <li>Food is <strong>not included</strong>.</li>
</ul>

<p><strong>Option 2</strong>: <a href="https://www.gameworks.com/?tId=1&amp;sId=14" target="_blank">GameWorks</a> in Tempe</p>

<p>What’s included…</p>

<ul>
  <li>One or two hour game card (depending on package)</li>
  <li>Private room for socializing and networking</li>
  <li>Choice of 7 different meal options</li>
  <li>Unlimited fountain drinks</li>
  <li>Each person can pay separately for their own extra food / drink.</li>
</ul>

<p>Leave your comments as to which you prefer. I would like to book the place on Monday.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Arizona" /><category term="Community" /><category term="Technology" /><summary type="html"><![CDATA[Phoenix Geek Tag was a minor success so I decided to put together another Geek outing in October. Most likely October 20th starting around 6pm. I have narrowed it down to two places.]]></summary></entry><entry><title type="html">Desert Code Camp is back</title><link href="https://www.josephguadagno.net/2009/09/14/desert-code-camp-is-back/" rel="alternate" type="text/html" title="Desert Code Camp is back" /><published>2009-09-14T02:00:00-07:00</published><updated>2009-09-14T02:00:00-07:00</updated><id>https://www.josephguadagno.net/2009/09/14/desert-code-camp-is-back</id><content type="html" xml:base="https://www.josephguadagno.net/2009/09/14/desert-code-camp-is-back/"><![CDATA[<p>The next Desert Code Camp is scheduled for Saturday, November 7th, 2009 at DeVry University.  Great sessions, networking, food, and WIFI (yes WIFI) will be provided. So come out learn, teach, network. It will be great.</p>

<p>Location:</p>

<p>DeVry University<br />
2149 W. Dunlap Ave (<a href="https://www.desertcodecamp.com/map.aspx" target="_blank">map</a>)<br />
Phoenix, Arizona 85021</p>

<p>Submit your talks, sessions, ideas for talks at the <a href="https://www.desertcodecamp.com" title="Desert Code Camp" target="_blank">Desert Code Camp</a> site.</p>

<p>On <a href="https://www.twitter.com" target="_blank">Twitter</a> follow <a href="https://www.twitter.com/desertcodecamp" title="Follow Desert Code Camp on Twitter" target="_blank">@DesertCodeCamp</a>, <a href="https://www.twitter.com/azgroups" title="Follow AZGroups on Twitter." target="_blank">@AZGroups</a>, and <a href="https://www.twitter.com/sevdnug" title="Follow SEVDNUG on Twitter." target="_blank">@SEVDNUG</a> for more information updates. Tweet up the event with the hashtag of <a href="https://twitter.com/#search?q=%23DCC09" title="Desert Code Camp hash tag. #DCC09" target="_blank">#DCC09</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="News" /><category term="Desert Code Camp" /><category term="Arizona" /><category term="Community" /><summary type="html"><![CDATA[The next Desert Code Camp is scheduled for Saturday, November 7th, 2009 at DeVry University.  Great sessions, networking, food, and WIFI (yes WIFI) will be provided. So come out learn, teach, network. It will be great.]]></summary></entry><entry><title type="html">Add an iCal feed to Sitefinity</title><link href="https://www.josephguadagno.net/2009/09/08/add-an-ical-feed-to-sitefinity/" rel="alternate" type="text/html" title="Add an iCal feed to Sitefinity" /><published>2009-09-08T08:22:34-07:00</published><updated>2009-09-08T08:22:34-07:00</updated><id>https://www.josephguadagno.net/2009/09/08/add-an-ical-feed-to-sitefinity</id><content type="html" xml:base="https://www.josephguadagno.net/2009/09/08/add-an-ical-feed-to-sitefinity/"><![CDATA[<p>iCal feeds let you share your and/or import your events that are stored in Sitefinity.  Here is a library that will let you generate an iCal feed from Sitefinity. Future versions of this library might have us filtering by categories or date.</p>

<p>iCal feed for Sitefinity <a href="/assets/downloads/CalendarHandler.zip">CalendarHandler</a></p>

<p>I’ve made a few changes since the last release to include some bug fixes.</p>

<ul>
  <li>Added Google and Outlook friendly fields in the feed file to describe the iCalendar file as well as the publisher.</li>
  <li>Added code to the <code class="language-plaintext highlighter-rouge">Location</code> and <code class="language-plaintext highlighter-rouge">Description</code> fields to keep them at 75 characters or less to comply with the iCal specifications.</li>
</ul>

<h2 id="installation">Installation</h2>

<h3 id="webconfig-changes">web.config changes</h3>

<p>First, you need to tell ASP.NET about your new handler. Open up the web config file and look for the <code class="language-plaintext highlighter-rouge">httpHandlers</code> section, it’s the parent is <code class="language-plaintext highlighter-rouge">system.web</code> and add the following:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;add</span> <span class="na">verb=</span><span class="s">"GET"</span> <span class="na">path=</span><span class="s">"GetICSFile.ashx"</span> <span class="na">type=</span><span class="s">"SEVDNUG.Web.HttpHandlers.CalendarHandler, App_Code"</span><span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Note: The path name can be changed to any legal value like <code class="language-plaintext highlighter-rouge">feeds/ical.ashx</code> or <code class="language-plaintext highlighter-rouge">ical.ashx</code>.</p>

<h3 id="files">Files</h3>

<p>Copy the <code class="language-plaintext highlighter-rouge">vCalendar.cs</code> and <code class="language-plaintext highlighter-rouge">CalendarHandler.cs</code> files to the <code class="language-plaintext highlighter-rouge">App_Code</code> directory of your Sitefinity installation.</p>

<h2 id="customize">Customize</h2>

<p>Getting the event data from Sitefinity and creating the iCalender objects is handled inside the <code class="language-plaintext highlighter-rouge">CalendarHandler</code> class.</p>

<p>The class assumes that your events provider is named <code class="language-plaintext highlighter-rouge">Events</code>, if not you will have to change line 25 of the class.</p>

<p>The class maps the following fields to an iCalendar event.</p>

<table>
  <thead>
    <tr>
      <th>iCalendar event</th>
      <th>Sitefinity Field</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Description</td>
      <td>Content</td>
    </tr>
    <tr>
      <td>Location</td>
      <td>LocationName (Meta Field)</td>
    </tr>
    <tr>
      <td>Summary</td>
      <td>Title (Meta field)</td>
    </tr>
    <tr>
      <td>Url</td>
      <td>UrlWithExtension</td>
    </tr>
    <tr>
      <td>DTSTART</td>
      <td>Start</td>
    </tr>
    <tr>
      <td>DTEND</td>
      <td>End</td>
    </tr>
  </tbody>
</table>

<p>I believe these meta values are the default meta keys from Sitefinity. If your values differ, you can modify them on lines 64 and 65.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Sitefinity" /><category term="Web" /><summary type="html"><![CDATA[iCal feeds let you share your and/or import your events that are stored in Sitefinity. Here is a library that will let you generate an iCal feed from Sitefinity. Future versions of this library might have us filtering by categories or date.]]></summary></entry><entry><title type="html">Sitefinity: Random Generic Content Control</title><link href="https://www.josephguadagno.net/2009/08/12/sitefinity-random-generic-content-control/" rel="alternate" type="text/html" title="Sitefinity: Random Generic Content Control" /><published>2009-08-12T11:19:45-07:00</published><updated>2009-08-12T11:19:45-07:00</updated><id>https://www.josephguadagno.net/2009/08/12/sitefinity-random-generic-content-control</id><content type="html" xml:base="https://www.josephguadagno.net/2009/08/12/sitefinity-random-generic-content-control/"><![CDATA[<p>Download <a href="/assets/downloads/RandomGCContent.zip">RandomGCContent</a></p>

<p><img src="/assets/images/posts/sponsor_thumb.png" alt="image-right" title="Sponsor Image" class="align-right" />
One of the many features of <a href="https://www.sitefinity.com" title="Sitefinity" target="_blank">Sitefinity</a> is the ability to create custom controls to customize the look and feel of your website. The website for the <a href="https://www.sevdnug.org" title="Southeast Valley .NET User Group" target="_blank">Southeast Valley .NET User Group</a> contains several custom controls. In the next few weeks, I will post all of the controls here. On to the Random Generic Content Control. The purpose of the control randomly displays items that are based on a Generic  Content provider. I use this control to randomly pick sponsors of the Southeast Valley .NET User Group to display on the home page. Here is a snapshot of the control in use.</p>

<h3 id="installing-the-control">Installing the Control</h3>

<p>Installing the control is a three-part process.</p>

<p>The first step is to add the control to Sitefinity.  This can be done one of three ways.  Which way you choose depends on your technical ability. Here is a list of options in order of least technical to most technical.</p>

<ol>
  <li>Add the class <code class="language-plaintext highlighter-rouge">RandomGCControl.cs</code> in the zip file to the <code class="language-plaintext highlighter-rouge">App_Code</code> folder of your Sitefinity project.</li>
  <li>Create a Visual Studio class library project, add the <code class="language-plaintext highlighter-rouge">RandomGCControl.cs</code> class to it. You will need to add the following references to the project; <code class="language-plaintext highlighter-rouge">Telerik.Cms</code>, <code class="language-plaintext highlighter-rouge">Telerik.Cms.Engine</code>, <code class="language-plaintext highlighter-rouge">Telerik.Cms.Web.UI</code>, <code class="language-plaintext highlighter-rouge">Telerik.Framework</code>, <code class="language-plaintext highlighter-rouge">Telerik.Personalization</code>, and <code class="language-plaintext highlighter-rouge">Telerik.Web.UI</code>. Compile the class, then upload it as documented by the Sitefinity article <a href="https://www.sitefinity.com/help/developer-manual/controls-adding-controls-to-sitefinity.html" title="Adding controls to Sitefinity">Adding Controls</a></li>
  <li>Create a separate class project as outlined in the step above then add a reference to this project to your Sitefinity project.</li>
</ol>

<p>The next step is let Sitefinity know about the new control. Note: this step is not required if you compiled the control and uploaded it through the Sitefinity interface. To do this you need to modify the web.config file.  The controls are listed in the cms/toolboxControls section. Add the following:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;add</span> <span class="na">name=</span><span class="s">"Random Generic Content"</span>
  <span class="na">section=</span><span class="s">"SEVDNUG"</span>
  <span class="na">type=</span><span class="s">"SEVDNUG.Web.Sitefinity.WebControls.RandomGCContent, SEVDNUG.Web.Sitefinity.WebControls"</span>
  <span class="na">description=</span><span class="s">"Displays a random generic content items"</span>
<span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>If you followed the steps, the next you go to edit a page in Sitefinity you should see the Random Generic Content control available in the SEVDNUG group.</p>

<h3 id="configuring-the-control">Configuring the Control</h3>

<p>The Random Generic Content control has four properties that you need configure.</p>

<table>
  <thead>
    <tr>
      <th>Property name</th>
      <th>Function</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Item List Template File (Appearance Section)</td>
      <td>Controls the overall look of items that appear. This file dictates the display of the generic content.</td>
    </tr>
    <tr>
      <td>Generic Content Details Url (Appearance section)</td>
      <td>This property points to the single page where you want Sitefinity to navigate to when an item is clicked. This is typically the same page that hosts this generic item.</td>
    </tr>
    <tr>
      <td>Number Of Items (Data section)</td>
      <td>Indicates how many generic items to display.</td>
    </tr>
    <tr>
      <td>Provider Name (Data Section)</td>
      <td>Indicates which generic item provider to use.</td>
    </tr>
  </tbody>
</table>

<h3 id="customizing-the-control">Customizing the Control</h3>

<p>To customize the display of the control, you will need to edit the file that you specified in the Item List Template File property of the control.  There is a sample template in the attached file.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"topSponsorsList"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;h2</span> <span class="na">class=</span><span class="s">"sf_listTitle"</span><span class="nt">&gt;</span>Random Content<span class="nt">&lt;/h2&gt;</span>
    <span class="nt">&lt;h3&gt;</span>
      <span class="nt">&lt;asp:Literal</span> <span class="na">ID=</span><span class="s">"GCTitle"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;&lt;/asp:Literal&gt;</span>
    <span class="nt">&lt;/h3&gt;</span>
    <span class="nt">&lt;asp:Repeater</span> <span class="na">ID=</span><span class="s">"GCRepeater"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;HeaderTemplate&gt;</span>
        <span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">"sf_simpleList"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;/HeaderTemplate&gt;</span>
      <span class="nt">&lt;ItemTemplate&gt;</span>
        <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"sf_simpleList"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;asp:Literal</span> <span class="na">ID=</span><span class="s">"Name"</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="nt">/&gt;</span>
        <span class="nt">&lt;/li&gt;</span>
      <span class="nt">&lt;/ItemTemplate&gt;</span>
      <span class="nt">&lt;FooterTemplate&gt;&lt;/ul&gt;&lt;/FooterTemplate&gt;</span>
    <span class="nt">&lt;/asp:Repeater&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The control will look for form field names that match the names of the meta keys for that provider and replace them with the value of the meta key.</p>

<p>Here is an example of the sponsor template used to generate the image above.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;asp:Repeater</span> <span class="na">ID=</span><span class="s">"GCRepeater"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span> 
  <span class="nt">&lt;HeaderTemplate&gt;&lt;ul</span> <span class="na">class=</span><span class="s">"sponsorList"</span><span class="nt">&gt;&lt;/HeaderTemplate&gt;</span>
  <span class="nt">&lt;ItemTemplate&gt;</span>
    <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"sponsorList"</span><span class="nt">&gt;</span>
      <span class="nt">&lt;asp:HyperLink</span> <span class="na">ID=</span><span class="s">"fullContent1"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">'&lt;asp:Literal ID="Company_Logo_Url" runat="server" /&gt;'</span>
          <span class="na">alt= </span><span class="s">'&lt;asp:Literal ID="Company_Name" runat="server" /&gt;'</span>
          <span class="na">width=</span><span class="s">"120"</span> <span class="na">height=</span><span class="s">"50"</span> <span class="na">onError=</span><span class="s">"img2txt(this)"</span> <span class="nt">/&gt;</span>
      <span class="nt">&lt;/asp:HyperLink&gt;</span>
    <span class="nt">&lt;/li&gt;</span>
  <span class="nt">&lt;/ItemTemplate&gt;</span>
  <span class="nt">&lt;FooterTemplate&gt;&lt;/ul&gt;&lt;/FooterTemplate&gt;</span>
<span class="nt">&lt;/asp:Repeater&gt;</span>
<span class="nt">&lt;script&gt;</span>
<span class="kd">function</span> <span class="nx">img2txt</span><span class="p">(</span><span class="nx">img</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">txt</span> <span class="o">=</span> <span class="nx">img</span><span class="p">.</span><span class="nx">alt</span><span class="p">;</span>
  <span class="nx">img</span><span class="p">.</span><span class="nx">parentNode</span><span class="p">.</span><span class="nx">innerHTML</span><span class="o">=</span><span class="nx">txt</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">&lt;/script&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Hopefully, you will find this control useful.  Look for some more controls and providers soon.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Sitefinity" /><category term="Web" /><summary type="html"><![CDATA[Download RandomGCContent]]></summary></entry><entry><title type="html">LaidOffCampPhx was a success!</title><link href="https://www.josephguadagno.net/2009/08/10/laidoffcampphx-was-a-success/" rel="alternate" type="text/html" title="LaidOffCampPhx was a success!" /><published>2009-08-10T09:12:00-07:00</published><updated>2009-08-10T09:12:00-07:00</updated><id>https://www.josephguadagno.net/2009/08/10/laidoffcampphx-was-a-success</id><content type="html" xml:base="https://www.josephguadagno.net/2009/08/10/laidoffcampphx-was-a-success/"><![CDATA[<p>Hats off to <a href="https://rachelreese.us/" target="_blank">Rachel Reese</a> for successfully organizing the first Laid Off Camp Phoenix. The event went smooth as could be.  I think the highlight of the event was watching all of the networking that went on during the event and after the event at Liberty Market.</p>

<p>Thanks to the sponsors below and all of the volunteers for contributing to the success of the event.</p>

<p>Below is a list of resources that were available in the program.</p>

<p><a href="https://millarian.com/" target="_blank">Curtis Miller</a> took some picture of the event and posted them on <a href="https://is.gd/28RA7" target="_blank">Flickr</a>.</p>

<h2 id="sponsors">Sponsors</h2>

<table>
  <thead>
    <tr>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Gilbert, AZ</td>
      <td>Economic Growth through innovation, collaboration and job-based economic development strategy.</td>
    </tr>
    <tr>
      <td>Flatterline</td>
      <td>Purveyors of kick a$ Web Development</td>
    </tr>
    <tr>
      <td>Hyatt Place Phoenix/Gilbert</td>
      <td>Stylish, Thoughtful, Savvy.</td>
    </tr>
    <tr>
      <td>TekSystems</td>
      <td>The Leading Technology Staffing and Services Company</td>
    </tr>
    <tr>
      <td>Gangplank</td>
      <td>Kryptonite for the Status Quo</td>
    </tr>
    <tr>
      <td>Hire Flyer Services</td>
      <td>Find the right job. Without all of the work.</td>
    </tr>
    <tr>
      <td>JC Printing</td>
      <td>Managing the Science of Printing</td>
    </tr>
  </tbody>
</table>

<h2 id="resources">Resources</h2>

<ul>
  <li><a href="https://www.phoenix.gov/econdev/phxwc/index.html">Phoenix Workforce Connection</a></li>
  <li><a href="https://www.bls.gov/audience/jobseekers.htm">Bureau of Labor Statistics: Resource for Job Seekers</a></li>
  <li><a href="https://hireflyer.com">HireFlyer Job Bank</a></li>
  <li><a href="https://gofreelance.com/freelance-jobs/">Go Freelancer</a></li>
  <li><a href="https://www.tentiltwo.com">10 til 2</a></li>
  <li><a href="https://www.ci.gilbert.az.us/busdev/">Working in Gilbert, Arizona</a></li>
</ul>

<h2 id="volunteers">Volunteers</h2>

<ul>
  <li>Perri Collins</li>
  <li>Xanthe Horste</li>
  <li>Charlene Kingston</li>
  <li>Derek Neighbors</li>
  <li>Evo Terra</li>
  <li>Lorin Twaits</li>
  <li>Debbie Walker</li>
  <li>Ann Watson-Barber</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="Articles" /><category term="Community" /><category term="Arizona" /><summary type="html"><![CDATA[Hats off to Rachel Reese for successfully organizing the first Laid Off Camp Phoenix. The event went smooth as could be.  I think the highlight of the event was watching all of the networking that went on during the event and after the event at Liberty Market.]]></summary></entry><entry><title type="html">ASP.NET 3.5 Application Architecture and Design</title><link href="https://www.josephguadagno.net/2008/11/24/asp-net-3-5-application-architecture-and-design/" rel="alternate" type="text/html" title="ASP.NET 3.5 Application Architecture and Design" /><published>2008-11-24T14:58:36-07:00</published><updated>2008-11-24T14:58:36-07:00</updated><id>https://www.josephguadagno.net/2008/11/24/asp-net-3-5-application-architecture-and-design</id><content type="html" xml:base="https://www.josephguadagno.net/2008/11/24/asp-net-3-5-application-architecture-and-design/"><![CDATA[<p>Although the title of this book states the book is for ASP.NET 3.5 application architecture and design, you will be able to take these concepts well beyond ASP.NET.  The concepts and technics that this author conveys can be used for all Windows .NET development.  The author starts out explaining basic 1 tier architecture and eventually graduates you to a five-tier architecture.</p>

<p>The book covers ER diagrams, domain modeling, common design patterns, SOA, WCF, and even database design.  This book is a good starter book for those of you that want to move out of the developer role and start learning about basic principals in applications design.</p>

<p>There is only one downside the book and that is the formatting of the code samples.  There seems to be an issue with the spacing and tabs (at least in the book).  Other than that it is a good read.</p>

<p>You can purchase the book on the <a href="https://www.packtpub.com/application-architecture-and-design-for-asp-.net-3.5/book/mid/261108nprnz1" target="_blank">PackT Publishing site</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[Although the title of this book states the book is for ASP.NET 3.5 application architecture and design, you will be able to take these concepts well beyond ASP.NET. The concepts and technics that this author conveys can be used for all Windows .NET development. The author starts out explaining basic 1 tier architecture and eventually graduates you to a five-tier architecture.]]></summary></entry><entry><title type="html">slide:ology: The Art and Science of Creating Great Presentations</title><link href="https://www.josephguadagno.net/2008/10/01/slideology-the-art-and-science-of-creating-great-presentations/" rel="alternate" type="text/html" title="slide:ology: The Art and Science of Creating Great Presentations" /><published>2008-10-01T02:31:22-07:00</published><updated>2008-10-01T02:31:22-07:00</updated><id>https://www.josephguadagno.net/2008/10/01/slideology-the-art-and-science-of-creating-great-presentations</id><content type="html" xml:base="https://www.josephguadagno.net/2008/10/01/slideology-the-art-and-science-of-creating-great-presentations/"><![CDATA[<p>I typically post reviews of books based on technology topics like .NET, LINQ, etc. but I thought this book would be a could fit for the site.  This book is about creating effective PowerPoint slides which many technology professionals, like myself, do on a regular basis. On to the review…</p>

<p>The author breaks up the book into multiple chapters but not your typically kind of chapters.  These chapters are about the different parts that make up a slide; graphics, displaying data, diagrams, use of movement, etc.  Each chapter is typically closed with a Case Study which demonstrates the use of the technic or recommends. This is done by showing a before and after the presentation.  The author also reinforces her points effectively by using graphics, colors, and illustrations.</p>

<p>The book is chock full of handy tips and suggestions that anyone can use to create dazzling and effective PowerPoint slides.  If you present using PowerPoint, this is a must-have.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[I typically post reviews of books based on technology topics like .NET, LINQ, etc. but I thought this book would be a could fit for the site. This book is about creating effective PowerPoint slides which many technology professionals, like myself, do on a regular basis. On to the review…]]></summary></entry><entry><title type="html">LINQ in Action</title><link href="https://www.josephguadagno.net/2008/09/29/linq-in-action/" rel="alternate" type="text/html" title="LINQ in Action" /><published>2008-09-29T04:55:13-07:00</published><updated>2008-09-29T04:55:13-07:00</updated><id>https://www.josephguadagno.net/2008/09/29/linq-in-action</id><content type="html" xml:base="https://www.josephguadagno.net/2008/09/29/linq-in-action/"><![CDATA[<p>I think the expression is “<em>don’t cry over spilled milk…</em>”, well when my son spilled milk on this book I almost cried. Why would you cry over a book you ask? There are many reasons.  Not only does the book go into LINQ in depth (I know there is a <a href="https://www.amazon.com/gp/product/1933988363?ie=UTF8&amp;tag=beyondthebasic0e&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=1933988363" target="_blank">C# in Depth</a> book, but the authors cover all of the parts of the .NET framework that were created and needed for LINQ to function. In addition, all of the samples provided are both in C# and VB.NET. These samples are not your basic “Hello World” samples but useful reusable code snippets that get the point of the book section across.</p>

<p>The book starts off with an introduction to LINQ and the different versions of LINQ to <em>xxx,</em> more than enough to wet your whistle. The authors then go into the prerequisites of the .NET framework required, generics, anonymous types, and lambda expressions.  This chapter was so good I read it twice.  The third chapter discusses how LINQ is made up.  After this, the authors go in depth in describing the different types of LINQ, including LINQ to SQL, LINQ to Objects and LINQ to Amazon with useful examples.</p>

<p><strong>This book will be a reference book on my bookshelf.  I expect to refer to this book constantly.</strong></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[I think the expression is “don’t cry over spilled milk…”, well when my son spilled milk on this book I almost cried. Why would you cry over a book you ask? There are many reasons. Not only does the book go into LINQ in depth (I know there is a C# in Depth book, but the authors cover all of the parts of the .NET framework that were created and needed for LINQ to function. In addition, all of the samples provided are both in C# and VB.NET. These samples are not your basic “Hello World” samples but useful reusable code snippets that get the point of the book section across.]]></summary></entry><entry><title type="html">T-Mobile WiFi Calling</title><link href="https://www.josephguadagno.net/2008/08/25/t-mobile-wifi-calling/" rel="alternate" type="text/html" title="T-Mobile WiFi Calling" /><published>2008-08-25T12:47:13-07:00</published><updated>2008-08-25T12:47:13-07:00</updated><id>https://www.josephguadagno.net/2008/08/25/t-mobile-wifi-calling</id><content type="html" xml:base="https://www.josephguadagno.net/2008/08/25/t-mobile-wifi-calling/"><![CDATA[<p>A month or so ago I signed up for this new <a href="https://www.t-mobile.com/" target="_blank">T-Mobile</a> plan that allows you to use your cell/mobile phone to make unlimited calls from almost any WiFi connection.  The reason I say almost is that there are two types of WiFi connections it will not make, a secured WiFi network that you do not have the security key for and any “free” WiFi connections that require you to accept the terms of use (most public access points).  When I saw this deal I signed up almost immediately. The idea that you can be on the road using your normal cell phone tower and they walk into your house and seamlessly have the call transferred to your wireless network was awesome. So I purchased four <a href="https://www.t-mobile.com/shop/Phones/Cell-Phone-Detail.aspx?cell-phone=Samsung-Katalyst" target="_blank">Samsung Katalyst T-739</a> phones which have WiFI / <a href="https://en.wikipedia.org/wiki/Generic_Access_Network" target="_blank">UMA</a> calling built in and my journey began. Since then I have had two months of unreliable WiFi calling connectivity. My first connection attempts were with my <a href="https://www.airlink101.com/products/ar430w.php" target="_blank">AirLink AR430W SuperG Wireless router</a> that I purchased for $15.00 dollars at a local Frys Electronic store. With this router, I would get sporadic WiFi/UMA connectivity on all four phones.  Some of them would connect, some of them would not. I would get one of a few T-Mobile errors; W002 and W010.  After googling for days and weeks, there was not a lot of information about these errors on the Internet. Most of the errors pointed to Blackberry connectivity issues and that errors W002 and W010 means that the device could not get an IP address which was quite strange because of both laptops, my Wii and Xbox 360 all connected fine.  So I began my troubleshooting… First I tried to connect to a friend’s un-secure router, every connection attempt was successful and I was making calls but this was not legal. So I tried to un-secure my wireless network, this worked for some of the phones some of the time. I tried changing the network beacon timeout, this worked for some of the phones some of the times.  The phones would sometimes connect after rebooting the router but this was unacceptable.  After about a month I decided it was my cheap $15 dollar router, so I went out and purchased a <a href="https://trendnet.com/products/proddetail.asp?prod=110_TEW-631BRP&amp;cat=66" target="_blank">TrendNET TEW-631BRP 300Mbps Wireless N Broadband Router</a>. I continued to have the same issues as above so I went back to Google and found a few sites that suggested to turn off MAC filtering, this was not applicable to me, or to try to assign static IP addresses to the phone so I tried this but no dice.  After about six weeks of troubleshooting and phone tag with the T-Mobile WiFi calling help des,k we came up with a solution.</p>

<h2 id="the-solution">The solution</h2>

<p>It turned out that the sporadic access was because I had the router set to automatically determine a channel.  The T-Mobile WiFi/UMA service will only work with channels 1,6, or 11, which I why it connected to my neighbor’s router (channel 6). Here are all of the settings that you can apply to the router to make it function similarly to the T-Mobile branded LinkSys router.</p>

<table>
  <thead>
    <tr>
      <th>Setting</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Beacon Period</td>
      <td>100</td>
    </tr>
    <tr>
      <td>RTS Threshold</td>
      <td>2347</td>
    </tr>
    <tr>
      <td>Fragmentation Threshold</td>
      <td>2346</td>
    </tr>
    <tr>
      <td>DTIM Interval</td>
      <td>1</td>
    </tr>
    <tr>
      <td>Wireless Channel (try in this order)</td>
      <td>11, 6, 1</td>
    </tr>
    <tr>
      <td>For a secure network</td>
      <td>WPA 2 is preferred over WEP</td>
    </tr>
  </tbody>
</table>

<p>I hope this helps solve some of your problems and that T-Mobile documents this somewhere.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Technology" /><summary type="html"><![CDATA[A month or so ago I signed up for this new T-Mobile plan that allows you to use your cell/mobile phone to make unlimited calls from almost any WiFi connection.  The reason I say almost is that there are two types of WiFi connections it will not make, a secured WiFi network that you do not have the security key for and any “free” WiFi connections that require you to accept the terms of use (most public access points).  When I saw this deal I signed up almost immediately. The idea that you can be on the road using your normal cell phone tower and they walk into your house and seamlessly have the call transferred to your wireless network was awesome. So I purchased four Samsung Katalyst T-739 phones which have WiFI / UMA calling built in and my journey began. Since then I have had two months of unreliable WiFi calling connectivity. My first connection attempts were with my AirLink AR430W SuperG Wireless router that I purchased for $15.00 dollars at a local Frys Electronic store. With this router, I would get sporadic WiFi/UMA connectivity on all four phones.  Some of them would connect, some of them would not. I would get one of a few T-Mobile errors; W002 and W010.  After googling for days and weeks, there was not a lot of information about these errors on the Internet. Most of the errors pointed to Blackberry connectivity issues and that errors W002 and W010 means that the device could not get an IP address which was quite strange because of both laptops, my Wii and Xbox 360 all connected fine.  So I began my troubleshooting… First I tried to connect to a friend’s un-secure router, every connection attempt was successful and I was making calls but this was not legal. So I tried to un-secure my wireless network, this worked for some of the phones some of the time. I tried changing the network beacon timeout, this worked for some of the phones some of the times.  The phones would sometimes connect after rebooting the router but this was unacceptable.  After about a month I decided it was my cheap $15 dollar router, so I went out and purchased a TrendNET TEW-631BRP 300Mbps Wireless N Broadband Router. I continued to have the same issues as above so I went back to Google and found a few sites that suggested to turn off MAC filtering, this was not applicable to me, or to try to assign static IP addresses to the phone so I tried this but no dice.  After about six weeks of troubleshooting and phone tag with the T-Mobile WiFi calling help des,k we came up with a solution.]]></summary></entry><entry><title type="html">Visual Studio Theme Generator</title><link href="https://www.josephguadagno.net/2008/08/07/visual-studio-theme-generator/" rel="alternate" type="text/html" title="Visual Studio Theme Generator" /><published>2008-08-07T14:54:00-07:00</published><updated>2008-08-07T14:54:00-07:00</updated><id>https://www.josephguadagno.net/2008/08/07/visual-studio-theme-generator</id><content type="html" xml:base="https://www.josephguadagno.net/2008/08/07/visual-studio-theme-generator/"><![CDATA[<p>I’ve been looking to PIMP my IDE by changing some of the colors to it.  I came across this <a href="https://blogs.msdn.com/gduthie/archive/2008/08/07/visual-studio-theme-generator.aspx" target="_blank">post</a> on DevHammer which pointed <a href="https://frickinsweet.com/tools/Theme.mvc.aspx" title="https://frickinsweet.com/tools/Theme.mvc.aspx#" target="_blank">https://frickinsweet.com/tools/Theme.mvc.aspx#</a>. This web application allows you to create a Visual Studio theme based on three colors.</p>

<p>It is Frickin’ Sweet, as implied by the domain name.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="News" /><category term="Technology News" /><category term="Visual Studio" /><summary type="html"><![CDATA[I’ve been looking to PIMP my IDE by changing some of the colors to it.  I came across this post on DevHammer which pointed https://frickinsweet.com/tools/Theme.mvc.aspx#. This web application allows you to create a Visual Studio theme based on three colors.]]></summary></entry><entry><title type="html">New Sitefinity tool created</title><link href="https://www.josephguadagno.net/2008/08/07/new-sitefinity-tool-created/" rel="alternate" type="text/html" title="New Sitefinity tool created" /><published>2008-08-07T04:18:00-07:00</published><updated>2008-08-07T04:18:00-07:00</updated><id>https://www.josephguadagno.net/2008/08/07/new-sitefinity-tool-created</id><content type="html" xml:base="https://www.josephguadagno.net/2008/08/07/new-sitefinity-tool-created/"><![CDATA[<p>As some of you know, I have the <a href="https://www.sevdnug.org/" target="_blank">Southeast Valley .NET User Group</a> web site running on <a href="https://www.telerik.com/" target="_blank">Telerik’s</a> <a href="https://www.sitefinity.com/" target="_blank">Sitefinity</a> product.  The product itself is awesome but needs a few features here an there… One of which is a mechanism to get data into Sitefinity quickly. Luckily, Sitefinity provides a pretty extensive <a href="https://www.sitefinity.com/help/developer-manual" target="_blank">API</a> for you to develop against. So with some free time, the little I have, I put together a Data Importer tool for Sitefinity. This tool will allow you to import data from a Comma Separated file into any Sitefinity generic provider.</p>

<p>The project is open source and available at <a href="https://www.codeplex.com/SEVDNUGDataImporter" title="https://www.codeplex.com/SEVDNUGDataImporter" target="_blank">https://www.codeplex.com/SEVDNUGDataImporter</a> please report any problems or feature request there.</p>

<p>Enjoy!</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Sitefinity" /><category term="ASP.NET" /><category term=".NET" /><summary type="html"><![CDATA[As some of you know, I have the Southeast Valley .NET User Group web site running on Telerik’s Sitefinity product.  The product itself is awesome but needs a few features here an there… One of which is a mechanism to get data into Sitefinity quickly. Luckily, Sitefinity provides a pretty extensive API for you to develop against. So with some free time, the little I have, I put together a Data Importer tool for Sitefinity. This tool will allow you to import data from a Comma Separated file into any Sitefinity generic provider.]]></summary></entry><entry><title type="html">Managing Software Development with Trac and Subversion</title><link href="https://www.josephguadagno.net/2008/07/28/managing-software-development-with-trac-and-subversion/" rel="alternate" type="text/html" title="Managing Software Development with Trac and Subversion" /><published>2008-07-27T23:35:25-07:00</published><updated>2008-07-27T23:35:25-07:00</updated><id>https://www.josephguadagno.net/2008/07/28/managing-software-development-with-trac-and-subversion</id><content type="html" xml:base="https://www.josephguadagno.net/2008/07/28/managing-software-development-with-trac-and-subversion/"><![CDATA[<p>This book is short on the number of pages but not necessarily on content.  The books author approaches Software Development Project Management as a problem and offers up how you can use <a href="https://trac.edgewall.org/" target="_blank">Trac</a> and <a href="https://subversion.tigris.org/" target="_blank">Subversion</a> as a solution.</p>

<p>The book starts to talk about what Software Development Project Management is a how to break it up into its pieces.  It then proceeds to introduce how to make Software Development Project Management easier with <a href="https://trac.edgewall.org/" target="_blank">Trac</a>, <a href="https://subversion.tigris.org/" target="_blank">Subversion</a>, <a href="https://httpd.apache.org/" target="_blank">Apache Web Server</a> and <a href="https://comparitech.net/webdav" target="_blank">WebDAV</a>. The book then begins to describe how to use Trac and Subversion of some of the details of each.</p>

<p>Overall, the book is a good starting point if you want to add Trac and Subversion to your project.  There are two things that I would have liked to see in the book however.  The first is the use of <a href="https://tortoisesvn.tigris.org/" target="_blank">TortoiseSVN</a> client as opposed to the command line.  The second is less use of the command line, while this is OK for Linux developers and some Windows developers, most do not like to use command lines.</p>

<p>You can find more about the book from the Packt Publishing web site at <a href="https://www.packtpub.com/managing-software-development-with-trac-and-subversion/book" title="https://www.packtpub.com/managing-software-development-with-trac-and-subversion/book" target="_blank">https://www.packtpub.com/managing-software-development-with-trac-and-subversion/book</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book is short on the number of pages but not necessarily on content. The books author approaches Software Development Project Management as a problem and offers up how you can use Trac and Subversion as a solution.]]></summary></entry><entry><title type="html">Creating a new Sitefinity generic content module</title><link href="https://www.josephguadagno.net/2008/07/28/creating-a-new-sitefinity-generic-content-module/" rel="alternate" type="text/html" title="Creating a new Sitefinity generic content module" /><published>2008-07-27T20:39:00-07:00</published><updated>2008-07-27T20:39:00-07:00</updated><id>https://www.josephguadagno.net/2008/07/28/creating-a-new-sitefinity-generic-content-module</id><content type="html" xml:base="https://www.josephguadagno.net/2008/07/28/creating-a-new-sitefinity-generic-content-module/"><![CDATA[<p>So, I heard that the <a href="https://www.sitefinity.com/" target="_blank">Sitefinity</a> application had this generic content module that was pretty easy to use to create your own module with. Why do you need to create a new generic content based module you ask?  There a few answers to that question.  I created a new generic content based module because I wanted to track all of the discounts that were offered to the <a href="https://www.sevdnug.org/" target="_blank">Southeast Valley .NET user group</a> and since free time is a something I do not have lately, it was an easy choice.  You might also want to leverage the existing Sitefinity generic content module because you do not have any development resources available to.  As the name implies it is generic, so generic in fact, it is used by the blog feature, news feature and events feature. After a week’s worth of digging into the code and documentation, I was successful. So that you do not have to go through the pain, I will lay out the instructions for you.  This guide will require virtually no coding, just a little HTML markup, and some web.config changes. There are few steps involved in creating a new copy of the generic module. </p>

<p class="notice--danger">The first step is to BACKUP your web.config file and Sitefinity project, just to be safe.</p>

<p>Here is a list of other steps:</p>

<ul>
  <li><a href="#_Getting_Started">Getting Started</a></li>
  <li><a href="#_Modify_the_web.config">Modify the web.config file</a></li>
  <li><a href="#_Create_your_new">Create the new admin section</a></li>
  <li><a href="#_Create_you_new">Create the new control templates</a></li>
  <li><a href="#_Add_the_data">Add the data</a></li>
  <li><a href="#_Create_the_Sitefinity">Create the Sitefinity page to display the content</a></li>
  <li><a href="#_Summary">Summary</a></li>
</ul>

<h2 id="document-conventions">Document Conventions</h2>

<p>Throughout this document, you will see the text <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>. Whenever you see this text you should replace the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> with the new name of your module. For example, I created a discount module and whenever I saw <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> I replaced it with <em>Discount</em>.</p>

<h2 id="getting-started">Getting Started</h2>

<p>There are two bugs in Sitefinity 3.2 Service Pack 2 hotfix 1616 that cause potential problems with using multiple generic control providers and the ContentView control.  Here are the posted fixes for them.</p>

<h3 id="multiple-providers-fix">Multiple Providers Fix</h3>

<p>This bug does not allow for you to select multiple generic content providers. To fix this issue, check out the Sitefinity knowledge base article titled <a href="https://www.sitefinity.com/support/knowledge-base/kb-article/b1154K-bach-b1154T-cmm.aspx">Adding a custom provider for Generic Content</a></p>

<h3 id="the-settings-for-contentview-controls-are-reset-fix">The Settings for ContentView controls are reset fix</h3>

<p>To fix this issue, check out the Sitefinity knowledge base article titled <a href="https://www.sitefinity.com/support/knowledge-base/kb-article/b1154K-bace-b1154T-cmm.aspx">Settings applied to the Content View based controls are reset</a></p>

<h3 id="resource-files">Resource Files</h3>

<p>Sitefinity is built on top of the .NET framework, and this coding technology can localize text properties (and other attributes, such as tooltips) Within an ASP.NET page, you will see the string</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="p">&lt;%</span> <span class="err">$</span><span class="n">Resources</span><span class="p">:</span><span class="n">SearchItemsBy</span> <span class="p">%&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This code tells the ASP.NET engine to get the resource file text in the key SearchItemsBy. This language is based on the page’s locale. If this text was on the <code class="language-plaintext highlighter-rouge">ControlPanelInsert.ascx</code> control, ASP.NET would look within the <code class="language-plaintext highlighter-rouge">ControlPanelInsert.ascx.resx</code> file, unless you had localized versions then it would look in <code class="language-plaintext highlighter-rouge">ControlPanelInsert.ascx.&lt;_locale_&gt;.resx</code>. For more information, please read <a href="https://msdn.microsoft.com/en-us/library/fw69ke6f.aspx?WT.mc_id=DT-MVP-4024623" target="_blank">Walkthrough: Using Resources for Localization with ASP.NET</a> from MSDN. Keep this in mind while editing your Sitefinity controls later on.</p>

<h3 id="contentview-control-overview">ContentView Control Overview</h3>

<p>The <code class="language-plaintext highlighter-rouge">ContentView</code> control in Sitefinity does most of the work for displaying generic content on a control or page. The <code class="language-plaintext highlighter-rouge">ContentView</code> control displays data on the web control by “looking” for control names that match the Meta field names within the provider. If you have a Meta field called <strong>Product</strong> then the ContentView control will look for a control on the page with the name <strong>Product</strong> and populate the control with the value of the Meta field. When the ContentView control sees a control with one of these names it will populate them with special functions.</p>

<table>
  <thead>
    <tr>
      <th>Control name</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>fullContent1</td>
      <td>This will be used to create a hyperlink to the details or “SingleItem” version of this content item.</td>
    </tr>
    <tr>
      <td>fullContent2</td>
      <td>This is the same as fullContent1.</td>
    </tr>
    <tr>
      <td>content</td>
      <td>This will be used to display your generic content.</td>
    </tr>
    <tr>
      <td>CommentsLink</td>
      <td>This will be used to create a hyperlink to view the comments of the content</td>
    </tr>
    <tr>
      <td>CommentsCount</td>
      <td>This will be used to display the total number of comments on this content.</td>
    </tr>
    <tr>
      <td>Category</td>
      <td>This will be used to display the category of the content. If this is a hyperlink control, the contentview control, will place a hyperlink on the page that will allow you to view all of the content classified in that category.</td>
    </tr>
    <tr>
      <td>Tags</td>
      <td>For this you will need an ASP.NET Repeater control and the content view control will place the tags used for that content</td>
    </tr>
    <tr>
      <td>Bookmarks</td>
      <td>For this you will need an ASP.NET Repeater control and the content view control will place the tags used for that content</td>
    </tr>
  </tbody>
</table>

<p>The <code class="language-plaintext highlighter-rouge">ContentView</code> control has two properties under the appearance section that tell it what control to display on the web page when you are in List mode or Single Item mode. These two properties are <code class="language-plaintext highlighter-rouge">ItemListTemplatePath</code> and <code class="language-plaintext highlighter-rouge">SingleItemTemplatePath</code>, respectively.</p>

<h2 id="modify-the-webconfig-file">Modify the web.config file</h2>

<p class="notice--danger"><strong>Warning</strong> Before you begin, <strong>BACKUP</strong> your <code class="language-plaintext highlighter-rouge">web.config</code>. The smallest error or unclosed angle bracket will cause your Sitefinity application to stop working.</p>

<h3 id="add-the-new-provider">Add the new provider</h3>

<p>Locate the section in your <code class="language-plaintext highlighter-rouge">web.config</code> file that is titled <strong>cmsProvider</strong>. It looks something like this</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;cmsEngine</span> <span class="na">defaultProvider=</span><span class="s">"Generic_Content"</span><span class="nt">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Copy the element (section) whose name is <code class="language-plaintext highlighter-rouge">Generic_Content</code>. It should look something like this</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;add</span> <span class="na">name=</span><span class="s">"Generic_Content"</span>
  <span class="na">urlRewriteFormat=</span><span class="s">"[Publication_Date]/[Title].aspx"</span>
  <span class="na">urlDateTimeFormat=</span><span class="s">"yy-MM-dd"</span>
  <span class="na">urlWhitespaceChar=</span><span class="s">"_"</span>
  <span class="na">visible=</span><span class="s">"True"</span>
  <span class="na">defaultMetaField=</span><span class="s">"Name"</span>
  <span class="na">securityProviderName=</span><span class="s">""</span>
  <span class="na">allowLocalization=</span><span class="s">"False"</span>
  <span class="na">allowVersioning=</span><span class="s">"True"</span>
  <span class="na">allowWorkflow=</span><span class="s">"False"</span>
  <span class="na">allowComments=</span><span class="s">"false"</span>
  <span class="na">commentsModeration=</span><span class="s">"true"</span>
  <span class="na">versioningProviderName=</span><span class="s">""</span>
  <span class="na">connectionStringName=</span><span class="s">"GenericContentConnection"</span>
  <span class="na">type=</span><span class="s">"Telerik.Cms.Engine.Data.Providers.DefaultProvider, Telerik.Cms.Engine.Data"</span><span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Paste this element onto the next line of the web config. Change the following attributes.</p>

<table>
  <thead>
    <tr>
      <th>Attribute</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>name</td>
      <td><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></td>
    </tr>
    <tr>
      <td>urlRewriteFormat</td>
      <td>The way you would like your URLs to look.</td>
    </tr>
    <tr>
      <td>defaultMetaField</td>
      <td>The name of the default meta field. Note: This must match a value in the meta field that you create later; otherwise you will get a difficult to debug “<em>The given key is not present in the dictionary</em>” exception.</td>
    </tr>
  </tbody>
</table>

<p>The <code class="language-plaintext highlighter-rouge">urlRewriteFormat</code> field can comprise of any of your Meta field names.  It my case, I created the following.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>urlRewriteFormat="/[Company_Name]/[Product].aspx"
</pre></td></tr></tbody></table></code></pre></div></div>

<p>All of the other fields you are free to do with as you choose.  Here is the sample Discount provider.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;add</span> <span class="na">name=</span><span class="s">"Discounts"</span>
  <span class="na">urlRewriteFormat=</span><span class="s">"/[Company_Name]/[Product].aspx"</span>
  <span class="na">urlDateTimeFormat=</span><span class="s">"yy-MM-dd"</span>
  <span class="na">urlWhitespaceChar=</span><span class="s">"_"</span>
  <span class="na">visible=</span><span class="s">"True"</span>
  <span class="na">applicationName=</span><span class="s">"/Discounts"</span>
  <span class="na">defaultMetaField=</span><span class="s">"Product"</span>
  <span class="na">securityProviderName=</span><span class="s">""</span>
  <span class="na">allowLocalization=</span><span class="s">"False"</span>
  <span class="na">allowVersioning=</span><span class="s">"True"</span>
  <span class="na">allowWorkflow=</span><span class="s">"False"</span>
  <span class="na">allowComments=</span><span class="s">"false"</span>
  <span class="na">commentsModeration=</span><span class="s">"true"</span>
  <span class="na">versioningProviderName=</span><span class="s">""</span>
  <span class="na">connectionStringName=</span><span class="s">"GenericContentConnection"</span>
  <span class="na">type=</span><span class="s">"Telerik.Cms.Engine.Data.Providers.DefaultProvider, Telerik.Cms.Engine.Data"</span><span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="add-the-new-meta-fields">Add the new Meta fields</h3>

<p>Further down in the web.config file, you should find an element, add Meta keys for each field for your new module. For the discount module, here is what I added:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Discounts.Product"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"True"</span> <span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
<span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Discounts.Company_Name"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"True"</span> <span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
<span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Discounts.Company_Url"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"False"</span> <span class="na">sortable=</span><span class="s">"False"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
<span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Discounts.Product_Logo_Url"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"False"</span> <span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
<span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Discounts.Discount"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"False"</span> <span class="na">sortable=</span><span class="s">"False"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
<span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Discounts.Discount_Code"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"False"</span> <span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
<span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Discounts.Category"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"True"</span> <span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
<span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Discounts.Author"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"True"</span> <span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">"Joseph Guadagno"</span><span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Please note that the name of the module and the word before the period must match the module name. For the Discounts Module provider I discussed, the name should be Discounts as shown below this sentence: The important part is for each property/field you want you to need to add a line. You will notice the format is “PropertyName”. For more information on the attributes, check out the <a href="https://www.sitefinity.com/help/developer-manual/telerik.cms.engine-telerik.cms.engine.metainfo_members.html" target="_blank">Sitefinity developer’s documentation</a>. Save your <code class="language-plaintext highlighter-rouge">web.config</code> and this completes all necessary changes to the file.</p>

<h2 id="create-your-new-admin-section">Create your new admin section</h2>

<h3 id="create-the-folder-structure">Create the folder structure</h3>

<p>So you can interface with the Discounts Module, you will now need to create the template files by following these steps:</p>

<ol>
  <li>Open the Windows Explorer (or within Visual Studio).</li>
  <li>Navigate to your website’s root directory.</li>
  <li>Go to <code class="language-plaintext highlighter-rouge">~/Sitefinity/Admin/ControlTemplates</code> folder.</li>
  <li>Select the <code class="language-plaintext highlighter-rouge">Generic_Content</code> folder and paste it into the <code class="language-plaintext highlighter-rouge">~/Sitefinity/Admin/ControlTemplates</code> directory.</li>
  <li>Rename that folder to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>. In my example, I renamed it to Discounts.</li>
</ol>

<h4 id="file-list">File List</h4>

<table>
  <thead>
    <tr>
      <th>Old name</th>
      <th>New Name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>App_LocalResources</td>
      <td>All of the application resource files are found here. These files contain most notably the text properties for all of the controls on the template.  There should be one .resx for each .ascx file.  This is how .NET allows you to create localized versions of your applications.</td>
    </tr>
    <tr>
      <td>CategoriesField.ascx</td>
      <td>Provides a drop down list of categories to choose from.</td>
    </tr>
    <tr>
      <td>CategoriesManagement.ascx</td>
      <td>Provides the ability to add, remove and modify categories that are available to this generic content.</td>
    </tr>
    <tr>
      <td>CategoriesSelector.ascx</td>
      <td>A control to select the categories to filter your records by.</td>
    </tr>
    <tr>
      <td>CommandPanel.ascx</td>
      <td>This control is what is displayed on the left hand side of the Sitefinity administration console.  You should not need to modify this.</td>
    </tr>
    <tr>
      <td>CommentsEdit.ascx</td>
      <td>A control that provides the ability to edit the comments of your generic content.</td>
    </tr>
    <tr>
      <td>CommentsList.ascx</td>
      <td>A control that lists the comments for your generic content.</td>
    </tr>
    <tr>
      <td>CommentsView.ascx</td>
      <td>A control that displays the individual comment of your generic content from the comments list control.</td>
    </tr>
    <tr>
      <td>ContentSelector.ascx</td>
      <td>This control provides a means to let you select generic content.</td>
    </tr>
    <tr>
      <td>ContentVersionView.aspx</td>
      <td>This is the control that will display the version history when you click on the version tab.</td>
    </tr>
    <tr>
      <td>ControlPanelEdit.ascx</td>
      <td>This is the admin control that allows you to edit your generic content.</td>
    </tr>
    <tr>
      <td>ControlPanelInsert.ascx</td>
      <td>This is the admin control that allows you to insert your generic content.</td>
    </tr>
    <tr>
      <td>ControlPanelList.ascx</td>
      <td>This is the admin control that displays the generic content available.</td>
    </tr>
    <tr>
      <td>ControlPanelPermissions.ascx</td>
      <td>This control provides the security controls for your generic content.</td>
    </tr>
    <tr>
      <td>Design</td>
      <td>Folder for the designer templates. I do not think these are used yet.</td>
    </tr>
    <tr>
      <td>EditorTemplate.ascx</td>
      <td>Not sure of purpose.  Not sure of use.</td>
    </tr>
    <tr>
      <td>NewContentDialog.ascx</td>
      <td>When you are creating page and you drag the Generic Content Module onto it, this is the dialog when you click “Share this Content”</td>
    </tr>
    <tr>
      <td>SelectContentDialog.ascx</td>
      <td>Same idea as above, but this is when you click on “Select Shared Content”</td>
    </tr>
    <tr>
      <td>TagEditor.ascx</td>
      <td>This control allows you to edit an individual tag</td>
    </tr>
    <tr>
      <td>TagsManagement.ascx</td>
      <td>This control allows you to edit the tags available for your content.</td>
    </tr>
  </tbody>
</table>

<h3 id="modify-the-admin-files">Modify the admin files</h3>

<p>There are three files that you are going to want to modify to accommodate your new Meta fields. They are <code class="language-plaintext highlighter-rouge">ControlPanelList.ascx</code>, <code class="language-plaintext highlighter-rouge">ControlPanelInsert.ascx</code>, and <code class="language-plaintext highlighter-rouge">ControlPanelEdit.ascx</code>.  I listed them in the order that they should be modified for testing.</p>

<h4 id="controlpanellistascx">ControlPanelList.ascx</h4>

<p>As mentioned previously, the ControlPanelList.ascx control is used to display the generic content in the admin panel. Of the three controls, the List control is the least likely to need modification. This control has two divs called <code class="language-plaintext highlighter-rouge">ToolsAll</code> and <code class="language-plaintext highlighter-rouge">workArea</code>, which break up the workspace for listing the generic content.</p>

<p>ToolsAll contains the <code class="language-plaintext highlighter-rouge">createNewButton</code>, which allows you to create a new generic content item and the <code class="language-plaintext highlighter-rouge">searchInputs</code> section which allows you to search for generic content. The choices for the search criteria depend on whether you make the Meta data “searchable” in the <code class="language-plaintext highlighter-rouge">web.config</code> file. Here is an example:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Discounts.Product"</span> <span class="na">searchable=</span><span class="s">"True"</span><span class="nt">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The div named “workArea” is broken up into three sections; <code class="language-plaintext highlighter-rouge">gridTitle</code> which provides the title for the grid, the <code class="language-plaintext highlighter-rouge">GridView1</code> which will display the grid of generic content items, and an ASP.NET PlaceHolder control named <code class="language-plaintext highlighter-rouge">emptyWindow</code>, which displays text when there are no content items in this provider. If you would like to add a new provider with additional Meta fields, then you would need to add additional columns to <code class="language-plaintext highlighter-rouge">GridView1</code> because this grid displays the meta fields.  These additional columns would help you narrow down the generic content that you want to modify.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;asp:GridView</span> <span class="na">ID=</span><span class="s">"GridView1"</span><span class="nt">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>To add a column to the grid control looks the <code class="language-plaintext highlighter-rouge">Columns</code> element, use the sample below this sentence:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;asp:BoundField</span> <span class="na">DataField=</span><span class="s">"Company_Name"</span>
  <span class="na">SortExpression=</span><span class="s">"Company_Name"</span>
  <span class="na">HeaderText=</span><span class="s">"&lt;%$Resources:Company_Name %&gt;"</span>
  <span class="na">HeaderStyle-CssClass=</span><span class="s">"GridHeader_SiteFinity"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;asp:BoundField</span> <span class="na">DataField=</span><span class="s">"Author"</span>
  <span class="na">SortExpression=</span><span class="s">"Author"</span>
  <span class="na">HeaderText=</span><span class="s">"&lt;%$Resources:Author %&gt;"</span>
  <span class="na">HeaderStyle-CssClass=</span><span class="s">"GridHeader_SiteFinity"</span> <span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>In this sample, I removed the <code class="language-plaintext highlighter-rouge">Description</code> field because I am not using it. I added a <code class="language-plaintext highlighter-rouge">Company_Name</code> column because this was a new meta field I added to the <code class="language-plaintext highlighter-rouge">web.config</code> file. Be sure to remove any unused Meta data fields or else you will get a runtime exception.</p>

<h4 id="controlpanellistascxresx">ControlPanelList.ascx.resx</h4>

<p>Here are the contents of the resource file with some modifications.</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AllContentItems</td>
      <td>All <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></td>
    </tr>
    <tr>
      <td>Author</td>
      <td>Author</td>
    </tr>
    <tr>
      <td>CheckGenericContentFAQ</td>
      <td>Check <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> FAQs</td>
    </tr>
    <tr>
      <td>Company_Name</td>
      <td>Company name</td>
    </tr>
    <tr>
      <td>CreateNewItem</td>
      <td>Create a <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></td>
    </tr>
    <tr>
      <td>CreateYourFirstContent</td>
      <td>Create your first <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></td>
    </tr>
    <tr>
      <td>CreateYourFirstContentTooltip</td>
      <td><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></td>
    </tr>
    <tr>
      <td>Delete</td>
      <td>Delete</td>
    </tr>
    <tr>
      <td>Description</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Edit</td>
      <td>Edit</td>
    </tr>
    <tr>
      <td>For</td>
      <td>for</td>
    </tr>
    <tr>
      <td>Name</td>
      <td>Name</td>
    </tr>
    <tr>
      <td>NoContent</td>
      <td>No <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> have been created yet.</td>
    </tr>
    <tr>
      <td>Or</td>
      <td>or</td>
    </tr>
    <tr>
      <td> </td>
      <td>Permissions FAQ</td>
    </tr>
    <tr>
      <td>Product</td>
      <td>Product name</td>
    </tr>
    <tr>
      <td>Search</td>
      <td>Search</td>
    </tr>
    <tr>
      <td>SearchItemsBy</td>
      <td>Search <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> by</td>
    </tr>
    <tr>
      <td>Status</td>
      <td>Status</td>
    </tr>
  </tbody>
</table>

<h4 id="controlpanelinsertascx">ControlPanelInsert.ascx</h4>

<p>As mentioned previously, the <code class="language-plaintext highlighter-rouge">ControlPanelInsert</code> control is used to insert new generic content items for this provider.</p>

<p>Just like the <code class="language-plaintext highlighter-rouge">ControlPanelList</code> control, this control is broken up into two divs, <code class="language-plaintext highlighter-rouge">ToolsAll</code> and <code class="language-plaintext highlighter-rouge">divWorkArea</code>. The <code class="language-plaintext highlighter-rouge">ToolsAll</code> contains the “back to …” link and the <code class="language-plaintext highlighter-rouge">divWorkArea</code> contains everything else.</p>

<p>There are three parts to the “divWorkArea,” the <code class="language-plaintext highlighter-rouge">sfMsg:MessageControl</code> control which displays messages based on the success or failure of saving the content, the mainForm which provides the editing surface, and the info div, which displays the FAQ text.</p>

<p>The mainForm again provides the area for which you insert your generic content. Some things that you need to know.  All of the Meta data fields need to be within the control <code class="language-plaintext highlighter-rouge">sfGCn:ContentMetaFields</code> and the <code class="language-plaintext highlighter-rouge">&lt;ItemTemplate&gt;</code> sub element otherwise the Sitefinity engine will not save the values. The fieldset classes are used to group the content together. Here is a sample fieldset with the Meta data fields for the discount content provider.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;h3&gt;&lt;asp:Literal</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Discount %&gt;"</span><span class="nt">&gt;&lt;/asp:Literal&gt;&lt;/h3&gt;</span>
<span class="nt">&lt;fieldset</span> <span class="na">class=</span><span class="s">"set"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"setIn"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;ol&gt;</span>
      <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"author"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Label</span> <span class="na">ID=</span><span class="s">"Label5″ AssociatedControlID="</span><span class="na">Company_Name</span><span class="err">"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Literal</span> <span class="na">ID=</span><span class="s">"Literal7″ runat="</span><span class="na">server</span><span class="err">"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Company_Name %&gt;"</span><span class="nt">&gt;&lt;/asp:Literal&gt;</span>
        <span class="nt">&lt;em</span> <span class="na">id=</span><span class="s">"Em2″ runat="</span><span class="na">server</span><span class="err">"</span><span class="nt">&gt;&lt;/em&gt;&lt;/asp:Label&gt;</span>
        <span class="nt">&lt;asp:TextBox</span> <span class="na">ID=</span><span class="s">"Company_Name"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Company_Name_Input %&gt;"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;&lt;/asp:TextBox&gt;</span>
      <span class="nt">&lt;/li&gt;</span>
      <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"author"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Label</span> <span class="na">ID=</span><span class="s">"Label6″ AssociatedControlID="</span><span class="na">Company_Url</span><span class="err">"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Literal</span> <span class="na">ID=</span><span class="s">"Literal8″ runat="</span><span class="na">server</span><span class="err">"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Company_Url %&gt;"</span><span class="nt">&gt;&lt;/asp:Literal&gt;</span>
        <span class="nt">&lt;em</span> <span class="na">id=</span><span class="s">"Em3″ runat="</span><span class="na">server</span><span class="err">"</span><span class="nt">&gt;&lt;/em&gt;&lt;/asp:Label&gt;</span>
        <span class="nt">&lt;asp:TextBox</span> <span class="na">ID=</span><span class="s">"Company_Url"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Company_Url_Input %&gt;"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;&lt;/asp:TextBox&gt;</span>
      <span class="nt">&lt;/li&gt;</span>
      <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"author"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Label</span> <span class="na">ID=</span><span class="s">"Label10″ AssociatedControlID="</span><span class="na">Product_Logo_Url</span><span class="err">"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Literal</span> <span class="na">ID=</span><span class="s">"Literal12″ runat="</span><span class="na">server</span><span class="err">"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Product_Logo_Url %&gt;"</span><span class="nt">&gt;&lt;/asp:Literal&gt;</span>
        <span class="nt">&lt;em</span> <span class="na">id=</span><span class="s">"Em7″ runat="</span><span class="na">server</span><span class="err">"</span><span class="nt">&gt;&lt;/em&gt;&lt;/asp:Label&gt;</span>
        <span class="nt">&lt;asp:TextBox</span> <span class="na">ID=</span><span class="s">"Product_Logo_Url"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Product_Logo_Url_Input %&gt;"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;/li&gt;</span>
      <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"author"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Label</span> <span class="na">ID=</span><span class="s">"Label8″ AssociatedControlID="</span><span class="na">Discount</span><span class="err">"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Literal</span> <span class="na">ID=</span><span class="s">"Literal10″ runat="</span><span class="na">server</span><span class="err">"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Discount %&gt;"</span><span class="nt">&gt;&lt;/asp:Literal&gt;</span>
        <span class="nt">&lt;em</span> <span class="na">id=</span><span class="s">"Em5″ runat="</span><span class="na">server</span><span class="err">"</span><span class="nt">&gt;&lt;/em&gt;&lt;/asp:Label&gt;</span>
        <span class="nt">&lt;asp:TextBox</span> <span class="na">ID=</span><span class="s">"Discount"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Discount_Input %&gt;"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;&lt;/asp:TextBox&gt;</span>
      <span class="nt">&lt;/li&gt;</span>
      <span class="nt">&lt;li</span> <span class="na">class=</span><span class="s">"author"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Label</span> <span class="na">ID=</span><span class="s">"Label9″ AssociatedControlID="</span><span class="na">Discount_Code</span><span class="err">"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
        <span class="nt">&lt;asp:Literal</span> <span class="na">ID=</span><span class="s">"Literal11″ runat="</span><span class="na">server</span><span class="err">"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Discount_Code %&gt;"</span><span class="nt">&gt;&lt;/asp:Literal&gt;</span>
        <span class="nt">&lt;em</span> <span class="na">id=</span><span class="s">"Em6″ runat="</span><span class="na">server</span><span class="err">"</span><span class="nt">&gt;&lt;/em&gt;&lt;/asp:Label&gt;</span>
        <span class="nt">&lt;asp:TextBox</span> <span class="na">ID=</span><span class="s">"Discount_Code"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:Discount_Code_Input %&gt;"</span> <span class="na">runat=</span><span class="s">"server /&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/div&gt;
&lt;/fieldset&gt;
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>Each of the individual field is wrapped in a <code class="language-plaintext highlighter-rouge">li</code> HTML tag. There is a <code class="language-plaintext highlighter-rouge">label</code>, and a <code class="language-plaintext highlighter-rouge">TextBox</code> for each Meta data field.</p>

<p>In order to display the categories for your specific generic content you will need to modify the</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;sfCtg:CategoriesField</span> <span class="na">ProviderName=</span><span class="s">"&lt;ModuleName&gt;"</span> <span class="na">ID=</span><span class="s">"Category"</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>tag.  You will need to add the ProviderName attribute tag and add the name of your generic provider.</p>

<h4 id="controlpanelinsertascxresx">ControlPanelInsert.ascx.resx</h4>

<p>Here are the contents of the ControlPanelInsert.ascx.resx file for the custom module. As you will notice, I created a resource entry for each of the Metadata fields created in the web.config file.</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AdditionalInfo</td>
      <td>Additional Info</td>
    </tr>
    <tr>
      <td>AdditionalInfoNote</td>
      <td>This information is not public. It is for your reference only.</td>
    </tr>
    <tr>
      <td>Author</td>
      <td>Author</td>
    </tr>
    <tr>
      <td>AuthorInput</td>
      <td>Author…</td>
    </tr>
    <tr>
      <td>BackToAllItems</td>
      <td>Cancel and go back</td>
    </tr>
    <tr>
      <td>Cancel</td>
      <td>Cancel</td>
    </tr>
    <tr>
      <td>Category</td>
      <td>Category</td>
    </tr>
    <tr>
      <td>Company_Name</td>
      <td>Company name</td>
    </tr>
    <tr>
      <td>Company_Name_Input</td>
      <td>enter the company name …</td>
    </tr>
    <tr>
      <td>Company_Url</td>
      <td>Company URL</td>
    </tr>
    <tr>
      <td>Company_Url_Input</td>
      <td>enter the company url …</td>
    </tr>
    <tr>
      <td>Content</td>
      <td>Text</td>
    </tr>
    <tr>
      <td>ContentEmpty</td>
      <td>The text cannot be empty!</td>
    </tr>
    <tr>
      <td>CreateThisItem</td>
      <td>Create this discount</td>
    </tr>
    <tr>
      <td>Description</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>DescriptionInput</td>
      <td>Description…</td>
    </tr>
    <tr>
      <td>Discount</td>
      <td>Discount</td>
    </tr>
    <tr>
      <td>Discount_Code</td>
      <td>Discount code</td>
    </tr>
    <tr>
      <td>Discount_Code_Input</td>
      <td>enter the discount code …</td>
    </tr>
    <tr>
      <td>Discount_Input</td>
      <td>enter the discount …</td>
    </tr>
    <tr>
      <td>GenericContentFAQ</td>
      <td>Discount FAQ</td>
    </tr>
    <tr>
      <td>MandatoryFields</td>
      <td>Mandatory fields</td>
    </tr>
    <tr>
      <td>Or</td>
      <td>or</td>
    </tr>
    <tr>
      <td>Product</td>
      <td>Product</td>
    </tr>
    <tr>
      <td>Product_Empty</td>
      <td>Product cannot be empty!</td>
    </tr>
    <tr>
      <td>Product_Input</td>
      <td>enter the product …</td>
    </tr>
    <tr>
      <td>Product_Label</td>
      <td>Product</td>
    </tr>
    <tr>
      <td>Product_Logo_Url</td>
      <td>Product Url</td>
    </tr>
    <tr>
      <td>Product_Logo_Url_Input</td>
      <td>enter the product url …</td>
    </tr>
    <tr>
      <td>Save</td>
      <td>Save</td>
    </tr>
    <tr>
      <td>Tags</td>
      <td>Tags</td>
    </tr>
  </tbody>
</table>

<h4 id="controlpaneleditascx">ControlPanelEdit.ascx</h4>

<p>As mentioned previously, the ControlPanelEdit control is used to edit existing generic content items for this provider.  You are also redirected to this control once the insert is successful. This control will need to be edited to include controls (textboxes, drop-down list, etc.) to edit the Metadata fields.</p>

<p>The <code class="language-plaintext highlighter-rouge">ControlPanelEdit</code> is very similar to the <code class="language-plaintext highlighter-rouge">ControlPanelInsert</code> except the <code class="language-plaintext highlighter-rouge">ControlPanelEdit</code> editing is wrapped in a <code class="language-plaintext highlighter-rouge">radTS:RadMultiPage</code> control. This <code class="language-plaintext highlighter-rouge">RadMultiPage</code> control allows you to display multiple pages or tabs on a single page by displaying one page and hiding the others.  The <code class="language-plaintext highlighter-rouge">RadMultiPage</code> page with the id of <code class="language-plaintext highlighter-rouge">ViewPage</code> displays the content and Metadata fields separately in a read-only mode. You will not need to edit this portion of the control or “page”.  The <code class="language-plaintext highlighter-rouge">RadMultiPage</code> page with this id of <code class="language-plaintext highlighter-rouge">EditPage</code> contains the editable part of the control.  This part of the control needs to be edited to add in the edit controls for each of your Metadata fields.  You will need to edit list similarly to the way the <code class="language-plaintext highlighter-rouge">ControlPanelEdit.ascx</code> file was updated.</p>

<p>In order to display the categories for your specific generic content you will need to modify the</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;sfCtg:CategoriesField</span> <span class="na">ProviderName=</span><span class="s">"&lt;ModuleName&gt;"</span> <span class="na">ID=</span><span class="s">"Category"</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>tag.  You will need to add the ProviderName attribute tag and add the name of your generic provider.</p>

<h4 id="controlpaneleditascxresx">ControlPanelEdit.ascx.resx</h4>

<p>Here are the contents of the ControlPanelEdit.ascx.resx file for the custom module. As you will notice, I created a resource entry for each of the Metadata fields created in the web.config file.</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Values</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AdditionalInfo</td>
      <td>Additional info</td>
    </tr>
    <tr>
      <td>AdditionalInfoNote</td>
      <td>This information is not public. It is for your reference only.</td>
    </tr>
    <tr>
      <td>Author</td>
      <td>Author</td>
    </tr>
    <tr>
      <td>AuthorInput</td>
      <td>Author…</td>
    </tr>
    <tr>
      <td>BackToAllItems</td>
      <td>Back to all <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></td>
    </tr>
    <tr>
      <td>Cancel</td>
      <td>Cancel</td>
    </tr>
    <tr>
      <td>Category</td>
      <td>Category</td>
    </tr>
    <tr>
      <td>ChangeLanguage</td>
      <td>Change Language</td>
    </tr>
    <tr>
      <td>Company_Name</td>
      <td>Company name</td>
    </tr>
    <tr>
      <td>Company_Url</td>
      <td>Company URL</td>
    </tr>
    <tr>
      <td>Content</td>
      <td>Text</td>
    </tr>
    <tr>
      <td>ContentEmpty</td>
      <td>The text cannot be empty!</td>
    </tr>
    <tr>
      <td>Date</td>
      <td>Date</td>
    </tr>
    <tr>
      <td>Description</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>DescriptionInput</td>
      <td>Description…</td>
    </tr>
    <tr>
      <td>Discount</td>
      <td>Discount</td>
    </tr>
    <tr>
      <td>Discount_Code</td>
      <td>Discount code</td>
    </tr>
    <tr>
      <td>Edit</td>
      <td>Edit</td>
    </tr>
    <tr>
      <td>EditThisItem</td>
      <td>Edit this discount</td>
    </tr>
    <tr>
      <td>GenericContentFAQ</td>
      <td>Discount FAQ</td>
    </tr>
    <tr>
      <td>History</td>
      <td>History</td>
    </tr>
    <tr>
      <td>IsEditingContent</td>
      <td>is editing this `` now!</td>
    </tr>
    <tr>
      <td>ItemVersions</td>
      <td><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> Versions</td>
    </tr>
    <tr>
      <td>MandatoryFields</td>
      <td>Mandatory fields</td>
    </tr>
    <tr>
      <td>Modifier</td>
      <td>Modifier</td>
    </tr>
    <tr>
      <td>Name</td>
      <td>Name</td>
    </tr>
    <tr>
      <td>NameEmpty</td>
      <td>Name cannot be empty!</td>
    </tr>
    <tr>
      <td>NameInput</td>
      <td>Name…</td>
    </tr>
    <tr>
      <td>Or</td>
      <td>or</td>
    </tr>
    <tr>
      <td>Product</td>
      <td>Product</td>
    </tr>
    <tr>
      <td>Product_Empty</td>
      <td>Product name cannot be empty!</td>
    </tr>
    <tr>
      <td>Product_Input</td>
      <td>Product…</td>
    </tr>
    <tr>
      <td>Product_Logo_Url</td>
      <td>Product Url</td>
    </tr>
    <tr>
      <td>Rollback</td>
      <td>Rollback</td>
    </tr>
    <tr>
      <td>Save</td>
      <td>Save</td>
    </tr>
    <tr>
      <td>SaveChanges</td>
      <td>Save changes</td>
    </tr>
    <tr>
      <td>Tags</td>
      <td>Tags</td>
    </tr>
    <tr>
      <td>Version</td>
      <td>Version</td>
    </tr>
    <tr>
      <td>VersionDateFormat</td>
      <td>{0:dd MMM yyyy, hh:mm}</td>
    </tr>
    <tr>
      <td>View</td>
      <td>View</td>
    </tr>
  </tbody>
</table>

<h2 id="create-the-new-control-templates">Create the new control templates</h2>

<p>The control templates are used for displaying the generic content on a user-facing page.</p>

<h3 id="create-the-folder-structure-1">Create the folder structure</h3>

<ul>
  <li>Open up Windows Explorer (or within Visual Studio).</li>
  <li>Navigate to where your website files are.</li>
  <li>Then navigate to <code class="language-plaintext highlighter-rouge">~/Sitefinity/</code> <code class="language-plaintext highlighter-rouge">ControlTemplates</code> folder.</li>
  <li>Select the <code class="language-plaintext highlighter-rouge">Generic_Content</code> folder.</li>
  <li>Copy it</li>
  <li>Paste it into the <code class="language-plaintext highlighter-rouge">~/Sitefinity/</code> <code class="language-plaintext highlighter-rouge">ControlTemplates</code> folder. Windows Explorer will give it a new name.  Rename it to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>.</li>
  <li>Copy the <code class="language-plaintext highlighter-rouge">~/Sitefinity/ControlTemplates/Events/SocialBookmark</code> folder to <code class="language-plaintext highlighter-rouge">~/Sitefinity/ControlTemplates/&lt;ModuleName&gt;</code> folder. For some reason, this was left out of the Sitefinity <code class="language-plaintext highlighter-rouge">Generic_Content</code> folder.</li>
</ul>

<p>Here comes the fun part. Depending on your liking, you can rename some of the files in the ~/Sitefinity/ ControlTemplates/<code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>.  Here is a list of the files that I would rename.</p>

<h4 id="file-list-1">File List</h4>

<table>
  <thead>
    <tr>
      <th>Filename</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ArchiveTemplate.ascx</code></td>
      <td>I think this is a holdover from the original News module.  We will not be using it.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">CategoriesList.ascx</code></td>
      <td>This is the control that will display the categories for your content.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">CommentsList.ascx</code></td>
      <td>This is the control that will display the comments.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ContentsViewItemList.ascx</code> or <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;ItemList.ascx</code></td>
      <td>This is the control that displays the content in a list form (multiple).</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ContentsViewSingleItem.ascx</code> or <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;SingleItem.ascx</code></td>
      <td>This is the control that displays the content in a single item (details).</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">genericContentCommonLayout.css</code> or <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;CommonLayout.css</code></td>
      <td>This file contains the CSS rules for this module.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">socialBookmarkTemplate.xml</code></td>
      <td>This contains the different social bookmarking sites available.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">TagsList.ascx</code></td>
      <td>This control displays all of your tag for the content.</td>
    </tr>
  </tbody>
</table>

<p>Most of the files do not need to be modified unless you want to.  We will concentrate on the ItemList and SingleItem files.  These files will need to be modified to display your Metadata fields.Modify the control template files</p>

<h3 id="modulenameitemlistascx"><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;ItemList.ascx</code></h3>

<p>As mentioned above in the file list section above, the <code class="language-plaintext highlighter-rouge">ItemList</code> control will be used to display the content items in a “repeater” or list view. As a suggestion, I would print out or keep handy the web.config section so you type the Metadata field names.</p>

<p>Let’s get started. If you renamed the <code class="language-plaintext highlighter-rouge">genericContentCommonLayout.css</code> you will need to update the FileName attribute of the <code class="language-plaintext highlighter-rouge">sfWeb:CssFileLink</code> element to match the new name.  It should look something link this.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;sfWeb:CssFileLink</span> <span class="na">ID=</span><span class="s">"CssFileLink1"</span>
  <span class="na">FileName=</span><span class="s">"~/Sitefinity/ControlTemplates/Discounts/discountsCommonLayout.css"</span>
  <span class="na">Media=</span><span class="s">"screen"</span>
  <span class="na">runat=</span><span class="s">"server"</span> <span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The rest of the changes pretty much come in the <code class="language-plaintext highlighter-rouge">asp:Repeater</code> control.  You can change the display to look how you wish. If you want to use any of the special fields like Tags or Categories, refer to the <code class="language-plaintext highlighter-rouge">ContentView </code>control overview earlier in this document.</p>

<h3 id="singleitemascx">‘<ModuleName>SingleItem.ascx'</ModuleName></h3>

<p>As mentioned above in the file list section above, the SingleItem control will be used to display the content item. As a suggestion, I would print out or keep handy the <code class="language-plaintext highlighter-rouge">web.config</code> section so you type the Metadata field names.</p>

<p>Let’s get started. If you renamed the <code class="language-plaintext highlighter-rouge">genericConentCommonLayout.css</code> you will need to update the FileName attribute of the sfWeb:CssFileLink element to match the new name.  It should look something like this.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;sfWeb:CssFileLink</span> <span class="na">ID=</span><span class="s">"CssFileLink1"</span>
  <span class="na">FileName=</span><span class="s">"~/Sitefinity/ControlTemplates/Discounts/discountsCommonLayout.css"</span>
  <span class="na">Media=</span><span class="s">"screen"</span>
  <span class="na">runat=</span><span class="s">"server"</span> <span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Everything will display within the first div.  You can change the display to look how you wish. If you want to use any of the special fields like Tags or Categories, refer to the ContentView control overview earlier in this document.</p>

<h2 id="add-the-data">Add the Data</h2>

<p>With all of the pages saved you should be able to load the Sitefinity administration section, click on the Generic Content section to start your work.</p>

<p>Click the change group dropdown list and you should see the new content group available. Select the new <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> item and you will see the ControlPanelList.ascx control displayed to the right.</p>

<p>Click on the “Create a new content item”, the name might be different if you changed the CreateNewItem item in the <code class="language-plaintext highlighter-rouge">ControlPanelList.ascx.resx</code> file. You will then be displayed with the <code class="language-plaintext highlighter-rouge">ControlPanelInsert.ascx</code> control which will allow you to add the content.</p>

<h2 id="create-the-sitefinity-page-to-display-the-content">Create the Sitefinity page to display the content</h2>

<p>Please refer to the Sitefinity user guide section titled “Working with Web Pages” for details on how to add a page to Sitefinity.  This section will cover the <code class="language-plaintext highlighter-rouge">ContentView</code> control specific changes, please review the Sitefinity use guide section “Working with user controls” for more information on adding controls to a page.  In my sample, I created a page called <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;.aspx</code> and added a <code class="language-plaintext highlighter-rouge">ContentView</code> control to the page, which can be found on the left-hand side of the Add Controls toolbox typically in the section named Generic Content.  Once you do this click the Edit hyperlink to set the properties of the <code class="language-plaintext highlighter-rouge">ContentView</code> control.</p>

<table>
  <thead>
    <tr>
      <th>Category</th>
      <th>Property</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Appearance</td>
      <td>ItemListTemplatePath</td>
      <td>This should point to the ContentViewItemList.ascx file that you modified earlier.<br />Example:<br /><code class="language-plaintext highlighter-rouge">~/Sitefinity/ControlTemplates/&lt;ModuleName&gt;/ContentViewItemList.ascx</code></td>
    </tr>
    <tr>
      <td>Appearance</td>
      <td>SingleItemListTemplatePath</td>
      <td>This should point to the ContentViewSingleItem.ascx file that you modified earlier.<br />Example:<br /><code class="language-plaintext highlighter-rouge">~/Sitefinity/ControlTemplates/&lt;ModuleName&gt;/ContentViewSingleItem.ascx</code></td>
    </tr>
    <tr>
      <td>Data</td>
      <td>ProviderName</td>
      <td><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></td>
    </tr>
    <tr>
      <td>Explicit Links</td>
      <td>SingleItemUrl</td>
      <td>This should point to the name of the page that the control is on.<br /><code class="language-plaintext highlighter-rouge">~/&lt;ModuleName&gt;.aspx</code></td>
    </tr>
    <tr>
      <td>Social Bookmarks</td>
      <td>SocialBookmarkImageFolder</td>
      <td><code class="language-plaintext highlighter-rouge">~/Sitefinity/ControlTemplates/&lt;ModuleName&gt;/SocialBookmark/</code></td>
    </tr>
    <tr>
      <td>Social Bookmarks</td>
      <td>SocialBookmarkTemplate</td>
      <td><code class="language-plaintext highlighter-rouge">~/Sitefinity/ControlTemplates/&lt;ModuleName&gt;/socialBookmarkTemplate.xml</code></td>
    </tr>
  </tbody>
</table>

<p>All of the other properties you can set to your liking.</p>

<h2 id="summary">Summary</h2>

<p>As you can see, building your own generic content provider using the Sitefinity generic content module is easy.  Follow this guide and you could have your new module like Sponsors, Products, or Books built. This should be everything that you need to get started expanding the generic content module of the Sitefinity product to meet your needs.</p>

<p>If there are any questions or comments, shoot me an email at <a href="mailto:jguadagno@hotmail.com">jguadagno@hotmail.com</a>.</p>

<p>Thanks to Joseph Anderson of Telerik for providing some helpful comments on this document.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Sitefinity" /><category term="ASP.NET" /><category term=".NET" /><summary type="html"><![CDATA[So, I heard that the Sitefinity application had this generic content module that was pretty easy to use to create your own module with. Why do you need to create a new generic content based module you ask?  There a few answers to that question.  I created a new generic content based module because I wanted to track all of the discounts that were offered to the Southeast Valley .NET user group and since free time is a something I do not have lately, it was an easy choice.  You might also want to leverage the existing Sitefinity generic content module because you do not have any development resources available to.  As the name implies it is generic, so generic in fact, it is used by the blog feature, news feature and events feature. After a week’s worth of digging into the code and documentation, I was successful. So that you do not have to go through the pain, I will lay out the instructions for you.  This guide will require virtually no coding, just a little HTML markup, and some web.config changes. There are few steps involved in creating a new copy of the generic module. ]]></summary></entry><entry><title type="html">Programming Windows Workflow Foundation: Practical WF Techniques and Examples using XAML and C#</title><link href="https://www.josephguadagno.net/2008/07/24/programming-windows-workflow-foundation-practical-wf-techniques-and-examples-using-xaml-and-c/" rel="alternate" type="text/html" title="Programming Windows Workflow Foundation: Practical WF Techniques and Examples using XAML and C#" /><published>2008-07-24T03:10:45-07:00</published><updated>2008-07-24T03:10:45-07:00</updated><id>https://www.josephguadagno.net/2008/07/24/programming-windows-workflow-foundation-practical-wf-techniques-and-examples-using-xaml-and-c</id><content type="html" xml:base="https://www.josephguadagno.net/2008/07/24/programming-windows-workflow-foundation-practical-wf-techniques-and-examples-using-xaml-and-c/"><![CDATA[<p>Well as you can see the title is quite but extremely accurate.  The book covers a “real world” use of Windows Workflow Foundation.  The author takes you through Windows Workflow Foundation by building a bug tracking application which is an ideal candidate for a workflow technology.  I think this approach is what makes this book stand apart from other Windows Workflow foundation books. It is short and concise and gives you a real application of the technology.  This is not a book filled with marketing fluff or the Hello World app (with the exception of the first chapter).</p>

<p>Overall, this book is a good read for those that want to get started in Windows Workflow Foundation.</p>

<p>The books web site: <a href="https://www.packtpub.com/windows-workflow-foundation-Csharp/book" title="https://www.packtpub.com/windows-workflow-foundation-Csharp/book">https://www.packtpub.com/windows-workflow-foundation-Csharp/book</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[Well as you can see the title is quite but extremely accurate. The book covers a “real world” use of Windows Workflow Foundation. The author takes you through Windows Workflow Foundation by building a bug tracking application which is an ideal candidate for a workflow technology. I think this approach is what makes this book stand apart from other Windows Workflow foundation books. It is short and concise and gives you a real application of the technology. This is not a book filled with marketing fluff or the Hello World app (with the exception of the first chapter).]]></summary></entry><entry><title type="html">How to create a Sitefinity custom module</title><link href="https://www.josephguadagno.net/2008/06/12/how-to-create-a-sitefinity-custom-module/" rel="alternate" type="text/html" title="How to create a Sitefinity custom module" /><published>2008-06-11T21:56:05-07:00</published><updated>2008-06-11T21:56:05-07:00</updated><id>https://www.josephguadagno.net/2008/06/12/how-to-create-a-sitefinity-custom-module</id><content type="html" xml:base="https://www.josephguadagno.net/2008/06/12/how-to-create-a-sitefinity-custom-module/"><![CDATA[<p>Download this document: <a href="https://sevdnug.org/Libraries/Sitefinity_Modules/SiteFinity%20Custom%20Module%20Creation.sflb">How to create a Sitefinity custom module</a></p>

<p>Download the source: <a href="https://sevdnug.org/Libraries/Sitefinity_Modules/SEVDNUG.Contacts.sflb">SEVDNUG.Contact.zip</a></p>

<p>This document outlines how to create a custom Sitefinity module by modifying the <code class="language-plaintext highlighter-rouge">SEVDNUG.Contacts</code> module.  The <code class="language-plaintext highlighter-rouge">SEVDNUG.Contacts</code> module was based on the <code class="language-plaintext highlighter-rouge">Samples.Contacts</code> module found on the Sitefinity blog.  This document gives you step by step directions on how to modify/tweak this <code class="language-plaintext highlighter-rouge">SEVDNUG.Contacts</code> module to implement your new custom module.  In addition, I attempt to explain what each file is used for. This sample module is constructed in a very similar to the way the modules of Sitefinity application are constructed. As a result, you should be able to use parts of this document to figure out how to customize certain features of Sitefinity modules. After completing the <a href="#first-steps">First Steps</a> and <a href="#modify-the-code">Modify the Code</a> sections of this document, your new module will be available on your Sitefinity site.</p>

<p>If you have any questions or issues, feel free to contact me at jguadagno [at] hotmail.com</p>

<h2 id="document-conventions-">Document conventions <!-- omit in toc --></h2>

<p>Text enclosed in <code class="language-plaintext highlighter-rouge">&lt;&gt;</code> brackets should be replaced by the type of text listed. So if you see <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;</code>.<code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>, the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> should be replaced by whatever you want to call your module, and the <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;</code> should be replaced by your company name like <code class="language-plaintext highlighter-rouge">SEVDNUG.Vendors</code>.</p>

<h2 id="contents">Contents</h2>

<ul>
  <li><a href="#contents">Contents</a></li>
  <li><a href="#first-steps">First Steps</a></li>
  <li><a href="#modify-the-code">Modify the Code</a>
    <ul>
      <li><a href="#companynamemodulename-project"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt; Project</code></a></li>
      <li><a href="#companynamemodulenamedata-project"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Data Project</code></a></li>
      <li><a href="#companynamemodulenameweb-project"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Web Project</code></a></li>
    </ul>
  </li>
  <li><a href="#sample-solution-structure">Sample Solution Structure</a>
    <ul>
      <li><a href="#companynamemodulename"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;</code></a></li>
      <li><a href="#companynamemodulenamedata"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Data</code></a></li>
      <li><a href="#companynamemodulenamewebsite"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Website</code></a></li>
    </ul>
  </li>
  <li><a href="#footnotes">Footnotes</a></li>
</ul>

<h2 id="first-steps">First Steps</h2>

<ul>
  <li>Rename the solution file to <code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;.sln</code></li>
  <li>Rename the folders
    <ul>
      <li>SEVDNUG.Contacts to <code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;</code></li>
      <li>SEVDNUG.Contacts.Data to <code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;.Data</code></li>
      <li>SEVDNUG.Contacts.Web to <code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;.Web</code></li>
    </ul>
  </li>
  <li>Rename the Visual Studio project files
    <ul>
      <li><code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;\SEVDNUG.Contacts.csproj</code> to <code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;\&lt;companyname&gt;.&lt;modulename&gt;</code></li>
      <li><code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;.Data\SEVDNUG.Contacts.Data.csproj</code> to <code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;.Data\&lt;companyname&gt;.&lt;modulename&gt;.Data</code></li>
    </ul>
  </li>
  <li>Open up the solution in a text editor like Notepad, search and replace <code class="language-plaintext highlighter-rouge">SEVDNUG.Contacts</code> to <code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;</code></li>
  <li>Open up the <code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;.sln</code> solution file in Visual Studio.</li>
  <li>Search and replace the text <code class="language-plaintext highlighter-rouge">SEVDNUG.Contacts</code> with <code class="language-plaintext highlighter-rouge">&lt;companyname&gt;.&lt;modulename&gt;</code></li>
  <li>Rename solution (optional)</li>
</ul>

<p>At the end of this article you will find a sample of what the solution would like.</p>

<h2 id="modify-the-code">Modify the Code</h2>

<h3 id="companynamemodulename-project"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt; Project</code></h3>

<p>Please follow these steps for this project prior to modifying the files.</p>

<ul>
  <li>Rename all of the <code class="language-plaintext highlighter-rouge">Contacts*.cs</code> files to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;*.cs</code>.</li>
  <li>Rename <code class="language-plaintext highlighter-rouge">IContact.cs</code> to <code class="language-plaintext highlighter-rouge">I&lt;ModuleName&gt;.cs</code></li>
  <li>Rename <code class="language-plaintext highlighter-rouge">WebControls\Contacts*.cs</code> to <code class="language-plaintext highlighter-rouge">WebControls\&lt;ModuleName&gt;*.cs</code></li>
  <li>Rename <code class="language-plaintext highlighter-rouge">WebControls\SingleContact*.cs</code> to <code class="language-plaintext highlighter-rouge">WebControls\Single&lt;ModuleName&gt;.cs</code></li>
  <li>Rename the references to Contact object and <code class="language-plaintext highlighter-rouge">IContact</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>, if necessary.</li>
</ul>

<h4 id="imodulenamecs-"><code class="language-plaintext highlighter-rouge">I&lt;ModuleName&gt;.cs</code> <!-- omit in toc --></h4>

<ul>
  <li>Add whatever properties will make up the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> object, these properties should match what you plan on storing in the database.</li>
</ul>

<h4 id="modulenamemodulecs-"><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Module.cs</code> <!-- omit in toc --></h4>

<p>This file contains the <code class="language-plaintext highlighter-rouge">Name</code>, <code class="language-plaintext highlighter-rouge">Title</code>, and <code class="language-plaintext highlighter-rouge">Description</code> of your module, as well as the security for your module.</p>

<ul>
  <li>Update the Description, Title and Name properties to whatever you want to be displayed.</li>
  <li>Rename the text, <code class="language-plaintext highlighter-rouge">Contact</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></li>
  <li><em>Note</em>: that nothing else needs to be changed, other than description, title and name properties</li>
</ul>

<h4 id="modulenameprovidercs-"><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Provider.cs</code> <!-- omit in toc --></h4>

<p>This class contains the “definition” of what your module provider implementation needs to support.</p>

<ul>
  <li>Rename the text, Contact to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></li>
  <li>Replace the content of the region “Contact Methods” with methods the fit your desired functionality.  Most of the contact - methods should be a good starting point.</li>
  <li>Add whatever additional methods you think your provider will need. Some examples are:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">Get&lt;ModuleName&gt;</code></li>
      <li><code class="language-plaintext highlighter-rouge">Save&lt;ModuleName&gt;</code></li>
      <li><code class="language-plaintext highlighter-rouge">Delete&lt;ModuleName&gt;</code></li>
      <li><code class="language-plaintext highlighter-rouge">Get&lt;ModuleName&gt;s</code></li>
    </ul>
  </li>
  <li>Under the properties section, add properties to the template files that your module will use. If you are making new templates, such as controls for the public facing side of the site such as the blog posts control, you would want to add them here. Here is an example:</li>
</ul>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Returns the path of external template for ControlPanel control in insert/edit mode</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">ControlPanelInsertEditTemplate</span>
<span class="p">{</span>
  <span class="k">get</span>
  <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="n">controlPanelInsertEditTemplate</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="modulenamemanagercs-"><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Manager.cs</code> <!-- omit in toc --></h4>

<p>This class provides is present in most Provider model patterns. It contain methods to interact with the provider.</p>

<ul>
  <li>Rename the text, <code class="language-plaintext highlighter-rouge">Contact</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></li>
  <li>Replace the content of the region “<strong>Contact Methods</strong>” with methods the fit your desired functionality.  Most of the contact - methods should be a good starting point. This should closely mirror the Provider methods.</li>
  <li>Add whatever additional methods you think your manager class will need.</li>
</ul>

<h4 id="webcontrols-">WebControls/* <!-- omit in toc --></h4>

<p>These classes provide the functionality to edit or display the data within Sitefinity.</p>

<ul>
  <li>Replace the text, <code class="language-plaintext highlighter-rouge">Contact</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></li>
  <li><em>Note</em>, you probably will have to manually change the Resources/Messages.resx content. TODO: Add reference to new localization article.</li>
</ul>

<h4 id="singlemodulenamecs-"><code class="language-plaintext highlighter-rouge">Single&lt;ModuleName&gt;.cs</code> <!-- omit in toc --></h4>

<p>This is used to display a single item (<code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>). This file provides the code behind to the <code class="language-plaintext highlighter-rouge">Single&lt;ModuleName&gt;.ascx</code> file.</p>

<ul>
  <li>Replace the text, <code class="language-plaintext highlighter-rouge">Contact</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></li>
  <li>Modify the <code class="language-plaintext highlighter-rouge">Single&lt;ModuleName&gt;Container</code> to include the fields defined in <code class="language-plaintext highlighter-rouge">I&lt;ModuleName&gt;.cs</code> file.</li>
  <li>Modify the <code class="language-plaintext highlighter-rouge">CreateChildControls</code> to reflect the controls that are available in the <code class="language-plaintext highlighter-rouge">layoutContainer</code></li>
</ul>

<h4 id="modulenamelistscs-"><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Lists.cs</code> <!-- omit in toc --></h4>

<p>This is used to display a list of items (<code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;s</code>)</p>

<ul>
  <li>Replace the text, <code class="language-plaintext highlighter-rouge">Contact</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></li>
  <li>Modify the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Repeater_ItemDataBound</code> method to display the properties of the object.</li>
</ul>

<h4 id="adminmodulenameeditorcs-"><code class="language-plaintext highlighter-rouge">Admin\&lt;ModuleName&gt;Editor.cs</code> <!-- omit in toc --></h4>

<p>This control is used for inserting new <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> objects or editing existing ones.</p>

<ul>
  <li>Replace the text, Contact to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></li>
  <li>Update the <code class="language-plaintext highlighter-rouge">CreateChildControls</code> method of the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Editor</code> class to match the properties available within your new module class.</li>
  <li>Update the <code class="language-plaintext highlighter-rouge">CreateNew&lt;ModuleName&gt;</code> method of the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Editor</code> class to match the properties available within your new module class.</li>
  <li>Update the <code class="language-plaintext highlighter-rouge">Update&lt;ModuleName&gt;</code> method of the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Editor</code> class to match the properties available within your new module class.</li>
  <li>Update the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;EditorContainer</code> class to the match the properties within your new module class. This should most likely match the <code class="language-plaintext highlighter-rouge">Single&lt;ModuleName&gt;.cs</code> class.</li>
</ul>

<p>Essentially, any properties that are available in your <code class="language-plaintext highlighter-rouge">I&lt;ModuleName&gt;</code> interface should have a line in the <code class="language-plaintext highlighter-rouge">CreateChildControls</code>, <code class="language-plaintext highlighter-rouge">CreateNew&lt;ModuleName&gt;</code>, <code class="language-plaintext highlighter-rouge">Update&lt;ModuleName&gt; methods</code>, as wells as properties in the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;EditorContainer</code> class.</p>

<h4 id="admincommandpanelcs-"><code class="language-plaintext highlighter-rouge">Admin\CommandPanel.cs</code> <!-- omit in toc --></h4>

<p>This module is used to display the side content in the Sitefinity admin interface. Other than changing its name, you do not need to change this control.</p>

<ul>
  <li>Replace the text, <code class="language-plaintext highlighter-rouge">Contact</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></li>
</ul>

<h4 id="admincontrolpanelcs-"><code class="language-plaintext highlighter-rouge">Admin\ControlPanel.cs</code> <!-- omit in toc --></h4>

<p>This module is used to display the side content in the Sitefinity admin interface. Other than changing its name, you do not need to change this control.</p>

<ul>
  <li>Replace the text, <code class="language-plaintext highlighter-rouge">Contact</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code></li>
</ul>

<h3 id="companynamemodulenamedata-project"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Data Project</code></h3>

<p>This assembly is responsible for all of the data access. At minimum it will have three classes, with this implementation, <code class="language-plaintext highlighter-rouge">DefaultProvider.cs</code>, <code class="language-plaintext highlighter-rouge">Variable.dbclass</code>, and <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;.dbclass</code>. Optionally, you can add <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;.cs</code> and <code class="language-plaintext highlighter-rouge">&lt;ModuleNames&gt;s.cs</code> to add additional data access methods. Any file with the extension “<code class="language-plaintext highlighter-rouge">.dbclass</code>” makes a table in your database.</p>

<p>Please follow these steps for this project prior to modifying the files.</p>

<ul>
  <li>Rename <code class="language-plaintext highlighter-rouge">Contact.*</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;*.cs</code></li>
  <li>Search and replace <code class="language-plaintext highlighter-rouge">Contact</code> with <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>.</li>
  <li>Add a reference to the new <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;</code> project.</li>
</ul>

<h4 id="defaultprovidercs-"><code class="language-plaintext highlighter-rouge">DefaultProvider.cs</code> <!-- omit in toc --></h4>

<p>This class provides the implementation of the provider for the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>. The private fields and following methods should not need to be changed; <code class="language-plaintext highlighter-rouge">SetVariable</code>, <code class="language-plaintext highlighter-rouge">GetVariable</code>, and <code class="language-plaintext highlighter-rouge">Initialize</code>. The first step to do is to replace the <code class="language-plaintext highlighter-rouge">Contact</code> with <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>. The next step is to implement any of the other methods required by <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Provider</code>.</p>

<h4 id="variabledbclass-"><code class="language-plaintext highlighter-rouge">Variable.dbclass</code> <!-- omit in toc --></h4>

<p>This file generates the database table for the variables table. This should not change.</p>

<h4 id="modulenamedbclass-"><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;.dbclass</code> <!-- omit in toc --></h4>

<p>This class generates the database for the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> table.  This file should change according to the way you want your database table. The Nolics library will create the table for you. Example:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="n">dbclass</span> <span class="n">ModuleName</span> <span class="p">[</span><span class="n">TableName</span><span class="o">=</span><span class="nv">"SEVDNUG_ModuleName"</span><span class="p">]{</span>
    <span class="k">primary</span> <span class="k">key</span> <span class="n">string</span> <span class="n">Application</span> <span class="p">[</span><span class="mi">50</span><span class="p">],</span> <span class="n">guid</span> <span class="n">ID</span><span class="p">[</span><span class="n">AutoGenGUID</span> <span class="o">=</span> <span class="k">true</span><span class="p">];</span>
    <span class="n">string</span> <span class="n">Name</span><span class="p">[</span><span class="k">Length</span><span class="o">=</span><span class="mi">100</span><span class="p">];</span>
    <span class="n">string</span> <span class="n">Url</span><span class="p">[</span><span class="k">Length</span><span class="o">=</span><span class="mi">255</span><span class="p">];</span>
    <span class="n">string</span> <span class="n">LogoUrl</span><span class="p">[</span><span class="k">Length</span><span class="o">=</span><span class="mi">255</span><span class="p">];</span>
    <span class="n">modified</span> <span class="nb">date</span> <span class="n">ModifiedOn</span><span class="p">;</span>
    <span class="n">created</span> <span class="nb">date</span> <span class="n">CreatedOn</span><span class="p">;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>For more info on modifying the dbclass library or how to modify this file, check the Nolics <a href="https://www.nolics.net/Docs4_2/Ref_dbclass.html" target="_blank">documentation</a>.</p>

<p>Where is the tutorial that tells you how to make tables?</p>

<h4 id="modulenamecs-"><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;.cs</code> <!-- omit in toc --></h4>

<p>This class will allow you to add additional properties and methods to the Nolics generated class.</p>

<h4 id="modulenamescs-"><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;s.cs</code> <!-- omit in toc --></h4>

<p>This class creates a dynamic query around the module table. Dynamic queries are typically used when there are custom table joins that you want to be available to any calling class. For more information on creating or using dynamic queries with Nolics please check out the Nolics <a href="https://www.nolics.com/Material2005/WT10_Queries.doc" target="_blank">documentation</a>.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">class</span> <span class="nc">ModuleNames</span><span class="p">:</span> <span class="n">Query</span><span class="p">&lt;</span><span class="n">ModuleName</span><span class="p">&gt;</span>
<span class="p">{</span>

<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="companynamemodulenameweb-project"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Web Project</code></h3>

<p>Please follow these steps for this project prior to modifying the files.</p>

<ul>
  <li>Rename the <code class="language-plaintext highlighter-rouge">/Sitefinity/ControlTemplates/Contacts</code> folder to <code class="language-plaintext highlighter-rouge">/Sitefinity/ControlTemplates/&lt;ModuleName&gt;</code></li>
  <li>Rename the file <code class="language-plaintext highlighter-rouge">/Sitefinity/ControlTemplates/&lt;ModuleName&gt;/ContactsListTemplate.ascx</code> to <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;ListTemplate.ascx</code></li>
  <li>Rename the file <code class="language-plaintext highlighter-rouge">/Sitefinity/ControlTemplates/&lt;ModuleName&gt;/SingleContactTemplate.ascx</code> to <code class="language-plaintext highlighter-rouge">Single&lt;ModuleName&gt;Template.ascx</code></li>
</ul>

<h4 id="webconfig-"><code class="language-plaintext highlighter-rouge">Web.Config</code> <!-- omit in toc --></h4>

<p>The <code class="language-plaintext highlighter-rouge">web.config</code> file for your Sitefinity installation will need to be modified to inform Sitefinity of the new module.  <em>Please note</em>: this section of the document requires that you modify your existing Sitefinity <code class="language-plaintext highlighter-rouge">web.config</code>.</p>

<p class="notice--danger">Please back up your <code class="language-plaintext highlighter-rouge">web.config</code> file before you change it!</p>

<p>There are three parts of the web.config file that need to be modified. They include the modules section, section group, and the meta fields section. The web.config provided in this example has a</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="c">&lt;!-- START REPLACE : Step # --&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>tag followed by a</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="c">&lt;!-- END REPLACE : Step # --&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>tag where text needs to be replaced or modified.</p>

<h4 id="notify-aspnet-of-the-new-section-group-">Notify ASP.NET of the new section group <!-- omit in toc --></h4>

<p>Search for</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="c">&lt;!-- START REPLACE : Step 1 --&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This section needs to be placed after the closing tag of the Telerik section group of your existing Sitefinity <code class="language-plaintext highlighter-rouge">web.config</code> file.</p>

<ul>
  <li>Replace the text of <code class="language-plaintext highlighter-rouge">SEVDNUG</code> with your company name. This should be what every use used for <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;</code>.</li>
  <li>Replace the text of <code class="language-plaintext highlighter-rouge">Contacts</code> with your module name. This should be what every use used for <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>.</li>
</ul>

<h4 id="tell-sitefinity-about-your-new-module-">Tell Sitefinity about your new module <!-- omit in toc --></h4>

<p>Search for</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="c">&lt;!-- START REPLACE : Step 2 --&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The line following this tag needs to be added to you telerik/framework/modules/ section of your existing Sitefinity web.config file.</p>

<ul>
  <li>Replace the text of <code class="language-plaintext highlighter-rouge">SEVDNUG</code> with your company name. This should be what every use used for <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;</code>.</li>
  <li>Replace the text of <code class="language-plaintext highlighter-rouge">Contacts</code> with your module name. This should be what every use used for <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>.</li>
</ul>

<h4 id="add-the-new-section-group-">Add the new section group <!-- omit in toc --></h4>

<p>Search</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="c">&lt;!-- START REPLACE : Step 3 --&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Everything between this tag and the</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="c">&lt;!-- END REPLACE : Step 3 --&gt;</span>

</pre></td></tr></tbody></table></code></pre></div></div>

<p>tag should be added to the end your exist web.config.</p>

<ul>
  <li>Replace the text of <code class="language-plaintext highlighter-rouge">SEVDNUG</code> with your company name. This should be what every use used for <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;</code>.</li>
  <li>Replace the text of <code class="language-plaintext highlighter-rouge">Contacts</code> with your module name. This should be what every use used for <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>.</li>
  <li>Replace the text of contact with the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code> for the following lines.</li>
</ul>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre>contactsPermissionsTemplate="~/Sitefinity/Admin/ControlTemplates/Contacts/ContactsPermissionsTemplate.ascx"
contactEditorTemplate="~/Sitefinity/Admin/ControlTemplates/Contacts/ContactEditorTemplate.ascx"
contactsListTemplate="~/Sitefinity/ControlTemplates/Contacts/ContactsListTemplate.ascx"
singleContactTemplate="~/Sitefinity/ControlTemplates/Contacts/SingleContactTemplate.ascx"
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Note, these xml attributes map to the properties that you defined in the <code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Provider</code> class. Here is an example:</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// Returns the path of external template for ContactsPermission</span>
<span class="c1">/// view set in web.config</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">ContactsPermissionsTemplate</span>
<span class="p">{</span>
  <span class="k">get</span>
  <span class="p">{</span>
    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="n">contactsPermissionsTemplate</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h4 id="add-the-new-meta-fields-">Add the new meta fields <!-- omit in toc --></h4>

<p>So Sitefinity can recognize the database columns that your module will make, you must declare them in the meta fields section of your web.config. Here is an example from the blogs module:</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;metafields&gt;</span>
  <span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"&lt;ModuleName&gt;.Title"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"True"</span><span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"&lt;ModuleName&gt;.Author"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span> <span class="na">searchable=</span><span class="s">"True"</span><span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"&lt;ModuleName&gt;.Publication_Date"</span> <span class="na">valueType=</span><span class="s">"DateTime"</span> <span class="na">visible=</span><span class="s">"True"</span><span class="na">searchable=</span><span class="s">"True"</span> <span class="na">sortable=</span><span class="s">"True"</span>   <span class="na">defaultValue=</span><span class="s">"#Now"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"&lt;ModuleName&gt;.BlogID"</span> <span class="na">valueType=</span><span class="s">"Guid"</span> <span class="na">visible=</span><span class="s">"False"</span> <span class="na">searchable=</span><span class="s">"True"</span><span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"&lt;ModuleName&gt;.Category"</span> <span class="na">valueType=</span><span class="s">"ShortText"</span> <span class="na">visible=</span><span class="s">"True"</span><span class="na">searchable=</span><span class="s">"True"</span> <span class="na">sortable=</span><span class="s">"True"</span> <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">&gt;&lt;/add&gt;</span>
<span class="nt">&lt;/metafields&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here is a breakdown of the properties:</p>

<table>
  <tbody>
    <tr>
      <td>Property</td>
      <td>Description</td>
    </tr>
    <tr>
      <td>Key</td>
      <td>This is the name of your module, then a period, then the name of the field.</td>
    </tr>
    <tr>
      <td>ValueType</td>
      <td>This property will depend on the type you set in your <code class="language-plaintext highlighter-rouge">I&lt;ModuleName&gt;.cs</code> file. If you set the type to GUID, then the value type will be GUID. This property should be short text for a paragraph under 256 characters and long text if it is above 256 characters.</td>
    </tr>
    <tr>
      <td>Visible</td>
      <td>This property should be true, unless this field is an ID column which should not be edited. If the valueType property is GUID, then the visible property should be false.</td>
    </tr>
    <tr>
      <td>Searchable</td>
      <td>If you would like your end user to search this property in the admin when in this module, then set this property to true. In the blogs module, as an example, you can search blogs by title.</td>
    </tr>
    <tr>
      <td>Sortable</td>
      <td>In the grid for the module in the admin, this will make the field sortable.</td>
    </tr>
    <tr>
      <td>DefaultValue</td>
      <td>This property is set to <code class="language-plaintext highlighter-rouge">null</code>, unless you want a value to be added all the time</td>
    </tr>
  </tbody>
</table>

<h4 id="public-and-private-templates-">Public and Private Templates <!-- omit in toc --></h4>

<p>So your end user can use the control on the page, you will need a public template. Generally, this consists of a repeater control with a series of controls inheriting from the <code class="language-plaintext highlighter-rouge">ItextControl</code> Interface. Sitefinity mainly uses literal and label controls and binds their ID property to a data field. In the blogs module, you could bind a label control to the author field like this:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;asp:Label</span> <span class="na">ID=</span><span class="s">”Author”</span> <span class="na">runat=</span><span class="s">”server”</span><span class="nt">&gt;&lt;/asp:label&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>When you are in the blogs module, you omit the word blogs and just use the word after the period.
The private templates are for users who click on the modules tab in the admin. These templates are used to add content to the modules and set permissions. Generally, you would only want to edit the templates that a person uses to add content. Adding and removing fields in the same as the public templates. Just add a label, as an example, and give its ID property a name from the meta fields section of the <code class="language-plaintext highlighter-rouge">web.config</code>.</p>

<h4 id="copy-to-sitefinity-">Copy to Sitefinity <!-- omit in toc --></h4>

<p>To deploy your module, you must copy the module theme, the newly created bin files and the templates by following these instructions:</p>

<ul>
  <li>Add a reference in your Sitefinity web project to the new <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;</code> and <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Data</code> assemblies or projects.</li>
  <li>Copy <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Website\Admin\ControlTemplates\&lt;ModuleName&gt;</code> to your Sitefinity directory<code class="language-plaintext highlighter-rouge">\Admin\ControlTemplates\&lt;ModuleName&gt;</code></li>
  <li>Copy <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Website\ControlTemplates\&lt;ModuleName&gt;</code> to your Sitefinity directory` ControlTemplates&lt;ModuleName&gt;`</li>
  <li>Copy <code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Website\Admin\Themes\Default\</code> to your Sitefinity directory<code class="language-plaintext highlighter-rouge">\Admin\Themes\Default\</code></li>
</ul>

<h2 id="sample-solution-structure">Sample Solution Structure</h2>

<h3 id="companynamemodulename"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;</code></h3>

<ul>
  <li>Properties
    <ul>
      <li>AssemblyInfo.cs</li>
    </ul>
  </li>
  <li>References
    <ul>
      <li>RadComboNox.Net2 <a href="#i">i</a></li>
      <li>RadGrid.Net2 <a href="#ii">ii</a></li>
      <li>RadMenu.Net2 <a href="#iii">iii</a></li>
      <li>RadTreeView.Net2 <a href="#iv">iv</a></li>
      <li>System</li>
      <li>System.configuration</li>
      <li>System.Data</li>
      <li>System.Drawing</li>
      <li>System.Web</li>
      <li>System.Xml</li>
      <li>Telerik.Cms</li>
      <li>Telerik.Cms.Web.UI</li>
      <li>Telerik.Framework</li>
      <li>Telerik.Security
        <ul>
          <li>Configuration</li>
        </ul>
      </li>
      <li><code class="language-plaintext highlighter-rouge">ConfigurationHelper.cs</code></li>
      <li><code class="language-plaintext highlighter-rouge">SectionHandler.cs</code>
        <ul>
          <li>Resources</li>
        </ul>
      </li>
      <li><code class="language-plaintext highlighter-rouge">CommandPanel.js</code></li>
      <li><code class="language-plaintext highlighter-rouge">Messages.resx</code>
        <ul>
          <li>Security</li>
        </ul>
      </li>
      <li><code class="language-plaintext highlighter-rouge">GlobalPermission.cs</code></li>
      <li><code class="language-plaintext highlighter-rouge">GlobalPermissions.cs</code>
        <ul>
          <li>WebControls
            <ul>
              <li>Admin
                <ul>
                  <li><code class="language-plaintext highlighter-rouge">AlphabetLinks.cs</code> <a href="#v">v</a></li>
                  <li><code class="language-plaintext highlighter-rouge">CommandPanel.cs</code></li>
                  <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Editor.cs</code></li>
                  <li><code class="language-plaintext highlighter-rouge">ControlPanel.cs</code></li>
                </ul>
              </li>
              <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;List.cs</code></li>
              <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;ListToolboxItem.cs</code></li>
              <li><code class="language-plaintext highlighter-rouge">Single&lt;ModuleName&gt;.cs</code></li>
              <li><code class="language-plaintext highlighter-rouge">Single&lt;ModuleName&gt;ToolboxItem.cs</code></li>
            </ul>
          </li>
          <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Manager.cs</code></li>
          <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;.Module.cs</code></li>
          <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;Provider.cs</code></li>
          <li><code class="language-plaintext highlighter-rouge">I&lt;ModuleName&gt;.cs</code></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h3 id="companynamemodulenamedata"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Data</code></h3>

<ul>
  <li>Properties
    <ul>
      <li>AssemblyInfo.cs</li>
    </ul>
  </li>
  <li>References
    <ul>
      <li>Nolics.Engine.v4.2 <a href="#vi">vi</a></li>
      <li><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;</code></li>
      <li>System</li>
      <li>System.configuration</li>
      <li>System.Data</li>
      <li>System.Xml</li>
      <li>Telerik.DataAccess</li>
      <li>Telerik.Framework</li>
      <li>Telerik.Security</li>
    </ul>
  </li>
  <li>Resources
    <ul>
      <li><code class="language-plaintext highlighter-rouge">Messages.resx</code></li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;.cs</code>  <a href="#vii">vii</a></li>
  <li><code class="language-plaintext highlighter-rouge">&lt;TableName&gt;.dbclass</code></li>
  <li><code class="language-plaintext highlighter-rouge">DefaultProvider.cs</code></li>
</ul>

<h3 id="companynamemodulenamewebsite"><code class="language-plaintext highlighter-rouge">&lt;CompanyName&gt;.&lt;ModuleName&gt;.Website</code></h3>

<ul>
  <li>Admin
    <ul>
      <li>ControlTemplates
        <ul>
          <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>
            <ul>
              <li><code class="language-plaintext highlighter-rouge">CommandPanelTemplate.ascx</code></li>
              <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;EditorTemplate.ascx</code></li>
              <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;sPermissions.ascx</code></li>
              <li><code class="language-plaintext highlighter-rouge">ControlPanelInsertEditTemplate.ascx</code></li>
              <li><code class="language-plaintext highlighter-rouge">ControlPanelListTemplate.ascx</code>
                <ul>
                  <li><code class="language-plaintext highlighter-rouge">App_LocalResources</code>
                    <ul>
                      <li>Any resource files</li>
                    </ul>
                  </li>
                </ul>
              </li>
            </ul>
          </li>
        </ul>
      </li>
      <li>Themes
        <ul>
          <li>Default
            <ul>
              <li><code class="language-plaintext highlighter-rouge">Modules.css</code></li>
            </ul>
          </li>
        </ul>
      </li>
      <li>ControlTemplates
        <ul>
          <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;</code>
            <ul>
              <li><code class="language-plaintext highlighter-rouge">&lt;ModuleName&gt;sListTemplate.ascx</code></li>
              <li><code class="language-plaintext highlighter-rouge">Single&lt;ModuleName&gt;Template.ascx</code></li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="footnotes">Footnotes</h2>

<table>
  <thead>
    <tr>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a id="i">i</a></td>
      <td>Optional, only required if you controls/templates use this control and you are licensed to use it.</td>
    </tr>
    <tr>
      <td><a id="ii">ii</a></td>
      <td>Optional, only required if you controls/templates use this control and you are licensed to use it.</td>
    </tr>
    <tr>
      <td><a id="iii">iii</a></td>
      <td>Optional, only required if you controls/templates use this control and you are licensed to use it.</td>
    </tr>
    <tr>
      <td><a id="iv">iv</a></td>
      <td>Optional, only required if you controls/templates use this control and you are licensed to use it.</td>
    </tr>
    <tr>
      <td><a id="v">v</a></td>
      <td>Optional, only if you wish to implement this command panel control.</td>
    </tr>
    <tr>
      <td><a id="vi">vi</a></td>
      <td>Optional, only if you use the Nolics library for data access.</td>
    </tr>
    <tr>
      <td><a id="vii">vii</a></td>
      <td>Optional, only if you want to add additional data access methods to the Nolics’ generated class.</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET" /><category term="Sitefinity" /><summary type="html"><![CDATA[Download this document: How to create a Sitefinity custom module]]></summary></entry><entry><title type="html">Southeast Valley .NET User Group site relaunched</title><link href="https://www.josephguadagno.net/2008/04/30/southeast-valley-net-user-group-site-relaunched/" rel="alternate" type="text/html" title="Southeast Valley .NET User Group site relaunched" /><published>2008-04-29T21:39:00-07:00</published><updated>2008-04-29T21:39:00-07:00</updated><id>https://www.josephguadagno.net/2008/04/30/southeast-valley-net-user-group-site-relaunched</id><content type="html" xml:base="https://www.josephguadagno.net/2008/04/30/southeast-valley-net-user-group-site-relaunched/"><![CDATA[<p>Come check out the new site <a href="https://www.sevdnug.org" target="_blank">https://www.sevdnug.org</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Arizona Technology News" /><category term="News" /><category term=".NET" /><category term="User Group" /><category term="Community" /><category term="Arizona" /><summary type="html"><![CDATA[Come check out the new site https://www.sevdnug.org]]></summary></entry><entry><title type="html">Telerik.Events</title><link href="https://www.josephguadagno.net/2008/04/25/telerik-events/" rel="alternate" type="text/html" title="Telerik.Events" /><published>2008-04-25T14:57:12-07:00</published><updated>2008-04-25T14:57:12-07:00</updated><id>https://www.josephguadagno.net/2008/04/25/telerik-events</id><content type="html" xml:base="https://www.josephguadagno.net/2008/04/25/telerik-events/"><![CDATA[<p>This module is responsible for adding, removing, deleting events from the Sitefinity system. This guide is meant to supplement the documentation that is available for Sitefinity. This document will cover the following:</p>

<ul>
  <li><a href="#files-for-events-module">Files for events module</a></li>
  <li><a href="#creating-an-event">Creating an event</a></li>
  <li><a href="#deleting-an-event">Deleting an event</a></li>
  <li><a href="#adding-meta-fields-to-an-event">Adding meta fields to an event</a></li>
  <li><a href="#adding-the-missing-rss-provider">Adding the missing RSS Provider</a></li>
</ul>

<p>As of the time when this was written, development documentation does not exist for the Telerik.Events assembly. The code samples where either “figured out” or derived from other samples. Thanks to Visual Studio®’s Intellisense® feature, Visual Studio®’s Object Browser and <a href="https://www.aisto.com/roeder/dotnet/" target="_blank">Reflector</a>, I figured out most of it. In order to work with events in Sitefinity, you must add a reference to the Telerik.Events assembly. This assembly is already included in the Sitefinity install; it is mentioned in case you want to create a separate assembly for your events customization. The Telerik.Events.EventsManager handles all of the event management. An instance of the EventManager can be created like this.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="kt">string</span> <span class="n">providerName</span> <span class="p">=</span> <span class="s">"Events"</span><span class="p">;</span>
<span class="n">EventsManager</span> <span class="n">eventsManager</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Telerik</span><span class="p">.</span><span class="n">Events</span><span class="p">.</span><span class="nf">EventsManager</span><span class="p">(</span><span class="n">providerName</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>After this, the eventsManager object will contain all the methods that you should need.</p>

<h2 id="files-for-events-module">Files for events module</h2>

<p>Like most (if not all) modules, the templates or user controls are keep in two directories underneath the <code class="language-plaintext highlighter-rouge">/Sitefinity</code> folder, the <code class="language-plaintext highlighter-rouge">Sitefinity\Admin\ControlTemplates\&lt;ModuleName&gt;</code>, in this case Events and <code class="language-plaintext highlighter-rouge">Sitefinity\ControlTemplates\&lt;ModuleName&gt;</code>. Within these directories, you will find a collection of files and one folder <code class="language-plaintext highlighter-rouge">App_Resources</code>. The <code class="language-plaintext highlighter-rouge">App_Resources</code> folder provides Sitefinity (or any .NET application) the ability to be localized. Keep in mind that if you want to change the text of a field or add a new field you will probably find the string in the corresponding .resx. In other words, if you are modifying <code class="language-plaintext highlighter-rouge">CommandPanel.ascx</code> you will find the resources to modify in <code class="language-plaintext highlighter-rouge">\App_Resources\CommandPanel.ascx.resx</code>. An example of this can be found in the section <a href="#adding-the-missing-rss-provider">Adding meta field to an event</a>. The developers of Sitefinity seem to be consistent with the naming of files, they sort of follow this syntax <code class="language-plaintext highlighter-rouge">_&lt;object&gt;&lt;function&gt;_.ascx</code>. So the insert form for an event can be found in the <code class="language-plaintext highlighter-rouge">_ControlPanelInsert.ascx_</code> file.</p>

<h3 id="admincontroltemplatesevents-files">Admin\ControlTemplates\Events Files</h3>

<p>This folder contains the files for the administrative portion of the events modules. While in most cases, except for adding meta fields to the event, you will not need to modify these files. However, if you are like me you like to know how things work.</p>

<h3 id="controltemplatesevents-files">ControlTemplates\Events Files</h3>

<p>This folder contains the files for the public/display portion of the events modules. These controls will be used to display the events on the public facing site.</p>

<table>
  <thead>
    <tr>
      <th>Filename</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ArchiveTemplate.ascx</td>
      <td> </td>
    </tr>
    <tr>
      <td>CategoriesList.ascx</td>
      <td>Displays a list of event categories and the number of events in that category. Not displayed in the “Community” template.</td>
    </tr>
    <tr>
      <td>CommentsList.ascx</td>
      <td>Displays the list of comments for an event.</td>
    </tr>
    <tr>
      <td>CommunityContentViewItemList.ascx</td>
      <td>This control is used by the Events page in the “Community” template to display a list of events.</td>
    </tr>
    <tr>
      <td>CommunityContentViewSingleItem.ascx</td>
      <td>This control is used to display and event that is clicked in the CommunityContentViewItemList control.</td>
    </tr>
    <tr>
      <td>ContentViewItemList.ascx</td>
      <td>This control is used by the Upcoming Events page in the “Community” template to display a list of events.</td>
    </tr>
    <tr>
      <td>ContentViewSingleItem.ascx</td>
      <td>This control is used to display and event that is clicked in the ContentViewItemList control.</td>
    </tr>
    <tr>
      <td>eventsCommonLayout.css</td>
      <td>The Cascading Style Sheet (CSS) used for the event pages.</td>
    </tr>
    <tr>
      <td>EventsScheduleView.ascx</td>
      <td>Displays the events in a calendar view.</td>
    </tr>
    <tr>
      <td>HomeContentViewItemList.ascx</td>
      <td>This control is used to display the events in on the home page for the “Community” template.</td>
    </tr>
    <tr>
      <td>TagsList.ascx</td>
      <td>Displays the lists of tags for an event.</td>
    </tr>
  </tbody>
</table>

<h2 id="creating-an-event">Creating an event</h2>

<p>In order to create an event in Sitefinity using the Telerik.Events assembly, you must follow a few steps.</p>

<ul>
  <li>Create an instance of the EventsManager class with the correct provider…</li>
</ul>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">EventsManager</span> <span class="n">eventsManager</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Telerik</span><span class="p">.</span><span class="n">Events</span><span class="p">.</span><span class="nf">EventsManager</span><span class="p">(</span><span class="n">providerName</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>Create a new generic content object…</li>
</ul>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">IContent</span> <span class="n">newEvent</span> <span class="p">=</span> <span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">CreateContent</span><span class="p">(</span><span class="s">"text/html"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>Update the meta data…</li>
</ul>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Title"</span><span class="p">,</span> <span class="n">eventTitle</span><span class="p">);</span>
<span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Content"</span><span class="p">,</span> <span class="n">eventDescription</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Optionally, get/create an event category, then update the metadata…</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="c1">// If the categoryName is not null or empty, update the category field.</span>
<span class="k">if</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">categoryName</span><span class="p">)</span> <span class="p">==</span> <span class="k">false</span><span class="p">)</span>
<span class="p">{</span>
  <span class="c1">// See if this event category exists</span>
  <span class="n">ICategory</span> <span class="n">category</span> <span class="p">=</span> <span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">GetCategory</span><span class="p">(</span><span class="n">categoryName</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">category</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="c1">// Create the category</span>
    <span class="n">category</span> <span class="p">=</span> <span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">CreateCategory</span><span class="p">(</span><span class="n">categoryName</span><span class="p">);</span>
    <span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">SaveCategory</span><span class="p">(</span><span class="n">category</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="c1">// Update the meta data</span>
  <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Category"</span><span class="p">,</span> <span class="n">categoryName</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>Save the content…</li>
</ul>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">SaveContent</span><span class="p">(</span><span class="n">newEvent</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="utility-method-for-creating-an-event">Utility method for creating an event</h3>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
</pre></td><td class="rouge-code"><pre><span class="k">protected</span> <span class="k">void</span> <span class="nf">CreateEvent</span><span class="p">(</span>
  <span class="kt">string</span> <span class="n">eventTitle</span><span class="p">,</span> 
  <span class="kt">string</span> <span class="n">eventDescription</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">contactName</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">contactEmail</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">contactPhone</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">contactCell</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">contactWeb</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">street</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">city</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">state</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">country</span><span class="p">,</span>
  <span class="n">DateTime</span> <span class="n">eventStartDate</span><span class="p">,</span> 
  <span class="n">DateTime</span> <span class="n">eventEndDate</span><span class="p">,</span>
  <span class="n">DateTime</span> <span class="n">eventExpirationDate</span><span class="p">,</span>
  <span class="n">DateTime</span> <span class="n">publicationDate</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">geomappingData</span><span class="p">,</span>
  <span class="kt">string</span> <span class="n">categoryName</span>
  
  <span class="p">{</span>
    <span class="kt">string</span> <span class="n">providerName</span> <span class="p">=</span> <span class="s">"Events"</span><span class="p">;</span>
    <span class="n">EventsManager</span> <span class="n">eventsManager</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Telerik</span><span class="p">.</span><span class="n">Events</span><span class="p">.</span><span class="nf">EventsManager</span><span class="p">(</span><span class="n">providerName</span><span class="p">);</span>
    <span class="n">IContent</span> <span class="n">newEvent</span> <span class="p">=</span> <span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">CreateContent</span><span class="p">(</span><span class="s">"text/html"</span><span class="p">);</span>

    <span class="c1">// Set the Generic Content Meta Data</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Title"</span><span class="p">,</span> <span class="n">eventTitle</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Content"</span><span class="p">,</span> <span class="n">eventDescription</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Street"</span><span class="p">,</span> <span class="n">street</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"City"</span><span class="p">,</span> <span class="n">city</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"State"</span><span class="p">,</span> <span class="n">state</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Country"</span><span class="p">,</span> <span class="n">country</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Contact_Name"</span><span class="p">,</span> <span class="n">contactName</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Contact_Email"</span><span class="p">,</span> <span class="n">contactEmail</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Contact_Phone"</span><span class="p">,</span> <span class="n">contactPhone</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Contact_Cell"</span><span class="p">,</span> <span class="n">contactCell</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Contact_Web"</span><span class="p">,</span> <span class="n">contactWeb</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Event_Start"</span><span class="p">,</span> <span class="n">eventStartDate</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Event_End"</span><span class="p">,</span> <span class="n">eventEndDate</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Publication_Date"</span><span class="p">,</span> <span class="n">publicationDate</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Expiration_Date"</span><span class="p">,</span> <span class="n">eventExpirationDate</span><span class="p">);</span>
    <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Geomapping_Data"</span><span class="p">,</span> <span class="n">geomappingData</span><span class="p">);</span>

    <span class="c1">// If the categoryName is not null or empty, update the category field.</span>
    <span class="k">if</span> <span class="p">(</span><span class="kt">string</span><span class="p">.</span><span class="nf">IsNullOrEmpty</span><span class="p">(</span><span class="n">categoryName</span><span class="p">)</span> <span class="p">==</span> <span class="k">false</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="c1">// See if this event category exists</span>
      <span class="n">ICategory</span> <span class="n">category</span> <span class="p">=</span> <span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">GetCategory</span><span class="p">(</span><span class="n">categoryName</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">category</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
      <span class="p">{</span>
        <span class="c1">// Create the category</span>
        <span class="n">category</span> <span class="p">=</span> <span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">CreateCategory</span><span class="p">(</span><span class="n">categoryName</span><span class="p">);</span>
        <span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">SaveCategory</span><span class="p">(</span><span class="n">category</span><span class="p">);</span>
      <span class="p">}</span>
      <span class="c1">// Update the meta data</span>
      <span class="n">newEvent</span><span class="p">.</span><span class="nf">SetMetaData</span><span class="p">(</span><span class="s">"Category"</span><span class="p">,</span> <span class="n">categoryName</span><span class="p">);</span>
    <span class="p">}</span>
  
    <span class="c1">// Save the Generic content</span>
    <span class="n">eventsManager</span><span class="p">.</span><span class="n">Content</span><span class="p">.</span><span class="nf">SaveContent</span><span class="p">(</span><span class="n">newEvent</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="deleting-an-event">Deleting an event</h2>

<p>In order to delete an event using the Telerik.Events assembly, you must get an instance of the <code class="language-plaintext highlighter-rouge">Telerik.Events.EventsManager</code> object. As you will see the code sample below, the eventsManager class has one method called <code class="language-plaintext highlighter-rouge">DeleteEvent()</code> which has 2 overloads. The first overload requires two parameters, the Event GUID which is the Event.Id and a boolean which indicates if the manager should delete the associated generic content.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">void</span> <span class="nf">DeleteEvent</span><span class="p">(</span><span class="n">Guid</span> <span class="n">ID</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">deleteContentItem</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The second overload requires two parameters, the Event which is the Event.Id and a boolean which indicates if the manager should delete the associated generic content.</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="k">public</span> <span class="k">void</span> <span class="nf">DeleteEvent</span><span class="p">(</span><span class="n">IEvent</span> <span class="n">_event</span><span class="p">,</span> <span class="kt">bool</span> <span class="n">deleteContentItem</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p class="notice--danger"><strong>Warning:</strong> This will <strong>delete all</strong> of the events in Sitefinity</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="kt">string</span> <span class="n">providerName</span> <span class="p">=</span> <span class="s">"Events"</span><span class="p">;</span>
<span class="n">EventsManager</span> <span class="n">eventsManager</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Telerik</span><span class="p">.</span><span class="n">Events</span><span class="p">.</span><span class="nf">EventsManager</span><span class="p">(</span><span class="n">providerName</span><span class="p">);</span>

<span class="n">IList</span> <span class="n">events</span> <span class="p">=</span> <span class="n">eventsManager</span><span class="p">.</span><span class="nf">GetEvents</span><span class="p">();</span>
<span class="k">foreach</span> <span class="p">(</span><span class="n">IEvent</span> <span class="n">eventItem</span> <span class="k">in</span> <span class="n">events</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">eventsManager</span><span class="p">.</span><span class="nf">DeleteEvent</span><span class="p">(</span><span class="n">eventItem</span><span class="p">.</span><span class="n">ID</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="adding-meta-fields-to-an-event">Adding meta fields to an event</h2>

<p>Adding meta fields to the events module is a 5 step process which involves editing the web.config file, editing the administration control templates, and the public display templates.</p>

<p>For this example, we will add a <em>Short Text</em> meta field call <code class="language-plaintext highlighter-rouge">ClickToAttendId</code> that we want to be displayed as part of an HTML hyperlink similar to this <code class="language-plaintext highlighter-rouge">https://www.clicktoattend.com/invitation.aspx?code=_ClickToAttendId_</code></p>

<h3 id="step-1-edit-the-webconfig-file"><strong>Step 1:</strong> Edit the web.config file</h3>

<p>Open the web.config file and search for</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;metaFields&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This is where Sitefinity loads the list of meta tags for each of the generic content modules. You should find a series of “add” XML elements. These “add” elements take 6 attributes, <code class="language-plaintext highlighter-rouge">key</code>, <code class="language-plaintext highlighter-rouge">valueType</code>, <code class="language-plaintext highlighter-rouge">searchable</code>, <code class="language-plaintext highlighter-rouge">sortable</code>, and <code class="language-plaintext highlighter-rouge">defaultValue</code>.</p>

<table>
  <thead>
    <tr>
      <th>Element</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>key</td>
      <td>The key is made up of two parts, <em>modulename</em>.<em>fieldName</em>. So Events.Title means the Title meta field of the events module.</td>
    </tr>
    <tr>
      <td>valueType</td>
      <td>The type of data that will be stored. The following are valid types: <code class="language-plaintext highlighter-rouge">Binary</code>, <code class="language-plaintext highlighter-rouge">Boolean</code>, <code class="language-plaintext highlighter-rouge">DateTime</code>, <code class="language-plaintext highlighter-rouge">FloatingPoint</code>, <code class="language-plaintext highlighter-rouge">Guid</code>, <code class="language-plaintext highlighter-rouge">Integer</code>, <code class="language-plaintext highlighter-rouge">LongText</code>, and <code class="language-plaintext highlighter-rouge">ShortText</code></td>
    </tr>
    <tr>
      <td>visible</td>
      <td>Indicates whether it will be shown in the admin section of the events module.</td>
    </tr>
    <tr>
      <td>searchable</td>
      <td>Indicates if you can search on this field.</td>
    </tr>
    <tr>
      <td>sortable</td>
      <td>Indicates if this field can be sorted on.</td>
    </tr>
    <tr>
      <td>defaultValue</td>
      <td>The default value for this field.</td>
    </tr>
  </tbody>
</table>

<p>Add a XML element to the</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;metaFields&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>section that looks like this</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;add</span> <span class="na">key=</span><span class="s">"Events.ClickToAttendId"</span>
  <span class="na">valueType=</span><span class="s">"ShortText"</span>
  <span class="na">visible=</span><span class="s">"True"</span>
  <span class="na">searchable=</span><span class="s">"False"</span>
  <span class="na">sortable=</span><span class="s">"True"</span>
  <span class="na">defaultValue=</span><span class="s">""</span><span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Save an close the web.config.</p>

<h3 id="step-2-edit-the-admincontroltemplateseventscontrolpaneleditascx"><strong>Step 2:</strong> Edit the Admin\ControlTemplates\Events\ControlPanelEdit.ascx</h3>

<p>Where you place the HTML markup depends on where you think the control needs to be. At the minimum it needs to be placed after the</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">”button_area</span> <span class="na">top</span><span class="err">”</span><span class="nt">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>code block and before the block…</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>  <span class="nt">&lt;/ItemTemplate&gt;</span>
<span class="nt">&lt;/sfGCn:ContentMetaFields&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>An example of the markup could look like this.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;h3&gt;</span>
  <span class="nt">&lt;asp:Literal</span> <span class="na">ID=</span><span class="s">"ltrClickToAttend"</span> <span class="na">runat=</span><span class="s">"server"</span> <span class="na">Text=</span><span class="s">"&lt;%$Resources:ClickToAttend %&gt;"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;/asp:Literal&gt;</span>
<span class="nt">&lt;/h3&gt;</span>
<span class="nt">&lt;fieldset</span> <span class="na">class=</span><span class="s">"set"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"setIn"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;asp:Label</span> <span class="na">ID=</span><span class="s">"lblClickToAttendId"</span> <span class="na">runat=</span><span class="s">"server"</span> 
      <span class="na">Text=</span><span class="s">'&lt;%$Resources:ClickToAttendId %&gt;'</span> <span class="na">AssociatedControlID=</span><span class="s">"ClickToAttendId"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;/asp:Label&gt;</span>
    <span class="nt">&lt;asp:TextBox</span> <span class="na">ID=</span><span class="s">"ClickToAttendId"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;&lt;/asp:TextBox&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/fieldset&gt;</span>
<span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"bottom"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;div&gt;</span>
    <span class="c">&lt;!-- --&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>The</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;h3&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>section is used for the title of grouping of the data.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;fieldset</span> <span class="na">class=</span><span class="s">”set”</span><span class="nt">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>element is used to contain the label and text box for the ClickToAttendId. Notice that there is a consistent naming theme for the HTML controls, <code class="language-plaintext highlighter-rouge">lbl_fieldName</code> for the label (this is optional), the text box must be the <code class="language-plaintext highlighter-rouge">fieldName</code> or Sitefinity will not be able to update it. Save the file. Open up <code class="language-plaintext highlighter-rouge">App_Resources\ControlPanelEdit.ascx.resx</code> and add in any of the resources that you referred to in this sample, namely ClickToAttendId.</p>

<h3 id="step-3-edit-the-admincontroltemplateseventscontrolpanelinsertascx"><strong>Step 3:</strong> Edit the Admin\ControlTemplates\Events\ControlPanelInsert.ascx</h3>

<p>The <code class="language-plaintext highlighter-rouge">ControlPanelInsert.ascx</code> should be edited in the same way that the <code class="language-plaintext highlighter-rouge">ControlPanelEdit.ascx</code> was edited.</p>

<h3 id="step-4-edit-the-controltemplateseventscommunitycontentviewsingleitemascx"><strong>Step 4:</strong> Edit the ControlTemplates\Events\CommunityContentViewSingleItem.ascx</h3>

<p>The markup can be placed anywhere inside the div block</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"sf_eventContent"</span><span class="nt">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>A sample of the markup could be.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;h4&gt;</span>Event Registration<span class="nt">&lt;/h4&gt;</span>
  <span class="nt">&lt;p&gt;</span>This event requires registration, please register
  <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">'https://www.ImComing.com/id.aspx?code=&lt;asp:Literal ID="ClickToAttendId" runat="server"&gt;&lt;/asp:Literal&gt;'</span><span class="nt">&gt;</span>
  here<span class="nt">&lt;/a&gt;</span>.
<span class="nt">&lt;/p&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Just as in the administrative templates, in order to display the metadata there should be a control on the form that matches the name of the metadata. Save the file. If necessary, open up the <code class="language-plaintext highlighter-rouge">App_Resources\ContentViewSingleItem.ascx.resx</code> file and add whatever resources you require.</p>

<h3 id="step-5-edit-the-controltemplateseventscontentviewsingleitemascx"><strong>Step 5:</strong> Edit the ControlTemplates\Events\ContentViewSingleItem.ascx</h3>

<p>For some reason, the markup of the ContentViewSingleItem is a bit different. The markup can be placed anywhere inside the div block</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"sf_eventContent"</span><span class="nt">&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>A sample of the markup could be.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;asp:PlaceHolder</span> <span class="na">ID=</span><span class="s">"plClickToAttendId"</span> <span class="na">runat=</span><span class="s">"server"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;h2&gt;</span>Event Registration<span class="nt">&lt;/h2&gt;</span>
  <span class="nt">&lt;p&gt;</span>Please click
    <span class="nt">&lt;asp:HyperLink</span> <span class="na">ID=</span><span class="s">"ClickToAttendId"</span> <span class="na">runat=</span><span class="s">"server"</span> 
      <span class="na">NavigateUrl=</span><span class="s">'&lt;% "https://www.clicktoattend.com/invitation.aspx?code=" + this.Text %&gt;'</span><span class="nt">&gt;</span>
    <span class="nt">&lt;/asp:HyperLink&gt;</span>
    to complete the registration for this event.
  <span class="nt">&lt;/p&gt;</span>
<span class="nt">&lt;/asp:PlaceHolder&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Save the file. If necessary, open up the <code class="language-plaintext highlighter-rouge">App_Resources\ContentViewSingleItem.ascx.resx</code> file and add whatever resources you require.</p>

<h2 id="adding-the-missing-rss-provider">Adding the missing RSS Provider</h2>

<p>Coming soon…</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Sitefinity" /><category term="Telerik" /><category term="Events" /><category term=".NET" /><summary type="html"><![CDATA[This module is responsible for adding, removing, deleting events from the Sitefinity system. This guide is meant to supplement the documentation that is available for Sitefinity. This document will cover the following:]]></summary></entry><entry><title type="html">Community Server Quickly: A Concise and Practical Guide to Installation, Administration, and Customization</title><link href="https://www.josephguadagno.net/2008/02/24/community-server-quickly-a-concise-and-practical-guide-to-installation-administration-and-customization/" rel="alternate" type="text/html" title="Community Server Quickly: A Concise and Practical Guide to Installation, Administration, and Customization" /><published>2008-02-23T17:04:05-07:00</published><updated>2008-02-23T17:04:05-07:00</updated><id>https://www.josephguadagno.net/2008/02/24/community-server-quickly-a-concise-and-practical-guide-to-installation-administration-and-customization</id><content type="html" xml:base="https://www.josephguadagno.net/2008/02/24/community-server-quickly-a-concise-and-practical-guide-to-installation-administration-and-customization/"><![CDATA[<p>This book covers all of the basic functionality of the <a href="https://communityserver.org/Default.aspx" target="_blank">Community Server</a> product from <a href="https://telligent.com/" target="_blank">Telligent</a>. Chapter 1 gives you a background of the product. Chapter 2 tells you what is required to use the product and how to get a copy of it. The rest of the chapters dive into the detail on the individual features of Community Server, like blogging, forums, RSS feeds, user management and more.</p>

<p>The author of the book was very diligent to capture every and explain the many configuration options for Community Server. This makes the book a worthwhile purchase for anyone who wants to implement Community Server.</p>

<p>This book is out and is available from <a href="https://www.packtpub.com/" target="_blank">Packt</a>. For more information about this book, please visit: <a href="https://www.packtpub.com/community-server/book" title="https://www.packtpub.com/community-server/book" target="_blank">https://www.packtpub.com/community-server/book</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book covers all of the basic functionality of the Community Server product from Telligent. Chapter 1 gives you a background of the product. Chapter 2 tells you what is required to use the product and how to get a copy of it. The rest of the chapters dive into the detail on the individual features of Community Server, like blogging, forums, RSS feeds, user management and more.]]></summary></entry><entry><title type="html">Beginners Guide to SQL Server Integration Services Using Visual Studio 2005</title><link href="https://www.josephguadagno.net/2008/02/23/beginners-guide-to-sql-server-integration-services-using-visual-studio-2005/" rel="alternate" type="text/html" title="Beginners Guide to SQL Server Integration Services Using Visual Studio 2005" /><published>2008-02-23T09:00:00-07:00</published><updated>2008-02-23T09:00:00-07:00</updated><id>https://www.josephguadagno.net/2008/02/23/beginners-guide-to-sql-server-integration-services-using-visual-studio-2005</id><content type="html" xml:base="https://www.josephguadagno.net/2008/02/23/beginners-guide-to-sql-server-integration-services-using-visual-studio-2005/"><![CDATA[<p>The back cover of the book states that the intended audience is …“beginners in the developer track who are looking to get exposure to SSIS”.  It was right on the money.</p>

<p>The book starts off with the great introduction to what SSIS is and what it is made up of. In then goes into, in depth, how to use Visual Studio 2005 to create, modify, debug and execute SSIS packages.  The rest of the book is a series of Hands-On labs guiding you through different task like transferring date from <em>xyz</em> source to SQL Server, which I find to be the best way for me to learn a topic. There is one chapter on scripting with unfortunately for my task a bit simple.  It would have been nice to see an advanced script, maybe something that calculated value and updated a column.</p>

<p>There were two things that the book was missing… It did not talk about the error handling or logging available in SSIS other than a mention that SSIS has it.</p>

<p>In summary, this is a great book for beginners to SSIS and DBA’s that what to start using Visual Studio to debug their SSIS packages.</p>

<p>This book is out and is available from <a href="https://www.packtpub.com/" target="_blank">Packt</a>. For more information about this book, please visit: <a href="https://www.packtpub.com/sql-server-integration-services-visual-studio-2005/book" target="_blank">www.packtpub.com/sql-server-integration-services-visual-studio-2005/book</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[The back cover of the book states that the intended audience is …“beginners in the developer track who are looking to get exposure to SSIS”. It was right on the money.]]></summary></entry><entry><title type="html">ASP.NET Data Presentation Controls Essentials</title><link href="https://www.josephguadagno.net/2008/02/18/asp-net-data-presentation-controls-essentials/" rel="alternate" type="text/html" title="ASP.NET Data Presentation Controls Essentials" /><published>2008-02-18T09:00:00-07:00</published><updated>2008-02-18T09:00:00-07:00</updated><id>https://www.josephguadagno.net/2008/02/18/asp-net-data-presentation-controls-essentials</id><content type="html" xml:base="https://www.josephguadagno.net/2008/02/18/asp-net-data-presentation-controls-essentials/"><![CDATA[<p>The book is a good starting point for people that was to learn the different ways of binding data from supported sources to ASP.NET controls.  The author does a pretty good job of explaining the basics of some of the data bindable controls like ListBox, DropDownList, etc.  There are whole chapters dedicated to the Repeater, DataList, DataGrid and DataView controls.</p>

<p>Unfortunately there are a few things that I did not like about the book. The first was the code, there were quite a few places where the code was not correct there were typos or incorrect characters to delimited strings. There where several locations in the code where double angle brackets (« ») where used for strings instead of double quotes (“).  Some of the code samples provided for the DataManager class were susceptible to SQL Injection attacks. Now I understand it is a sample but a little disclaimed should be made to break habits.  One last thing, some of code could benefit from some refactoring.</p>

<p>Overall, I think this books would be could a good reference point for data binding in Microsoft ASP.NET.</p>

<p>The website for the book: <a href="https://www.packtpub.com/asp-net-data-presentation-controls/book" target="_blank">https://www.packtpub.com/asp-net-data-presentation-controls/book</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[The book is a good starting point for people that was to learn the different ways of binding data from supported sources to ASP.NET controls.  The author does a pretty good job of explaining the basics of some of the data bindable controls like ListBox, DropDownList, etc.  There are whole chapters dedicated to the Repeater, DataList, DataGrid and DataView controls.]]></summary></entry><entry><title type="html">Huge Collection of Technology Posters</title><link href="https://www.josephguadagno.net/2008/01/24/huge-collection-of-technology-posters/" rel="alternate" type="text/html" title="Huge Collection of Technology Posters" /><published>2008-01-23T18:46:09-07:00</published><updated>2008-01-23T18:46:09-07:00</updated><id>https://www.josephguadagno.net/2008/01/24/huge-collection-of-technology-posters</id><content type="html" xml:base="https://www.josephguadagno.net/2008/01/24/huge-collection-of-technology-posters/"><![CDATA[<p>A <a href="https://blogs.msdn.com/cbowen/archive/2007/12/09/got-tech-posters.aspx" target="_blank">blog post</a> from <a href="https://blogs.msdn.com/cbowen/default.aspx" target="_blank">Chris Bowen</a> has been making the rounds.  It contains quite a few technology posters available.  Here is a short sampling…</p>

<ul>
  <li>.NET 3.5 Namespace</li>
  <li>Exchange Component Architecture</li>
  <li>Visual Basic 2008 Keybinding Poster</li>
  <li>Visual c++ 2006 Keybinding Poster</li>
  <li>Visual C# Keybinding Poster</li>
  <li>And many more…</li>
</ul>

<p>Check it out at <a href="https://blogs.msdn.com/cbowen/archive/2007/12/09/got-tech-posters.aspx" title="https://blogs.msdn.com/cbowen/archive/2007/12/09/got-tech-posters.aspx" target="_blank">https://blogs.msdn.com/cbowen/archive/2007/12/09/got-tech-posters.aspx</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Miscellaneous" /><category term="Technology" /><summary type="html"><![CDATA[A blog post from Chris Bowen has been making the rounds.  It contains quite a few technology posters available.  Here is a short sampling…]]></summary></entry><entry><title type="html">Microsoft Ajax library cheat sheets</title><link href="https://www.josephguadagno.net/2008/01/14/microsoft-ajax-library-cheat-sheets/" rel="alternate" type="text/html" title="Microsoft Ajax library cheat sheets" /><published>2008-01-14T15:40:38-07:00</published><updated>2008-01-14T15:40:38-07:00</updated><id>https://www.josephguadagno.net/2008/01/14/microsoft-ajax-library-cheat-sheets</id><content type="html" xml:base="https://www.josephguadagno.net/2008/01/14/microsoft-ajax-library-cheat-sheets/"><![CDATA[<p>I came across some <a href="https://aspnetresources.com/blog/ms_ajax_cheat_sheets_batch2.aspx" target="_blank">cheat sheets</a> from the Microsoft Ajax library.  They were created by <a href="https://aspnetresources.com/blog/default.aspx" target="_blank">Millan Negovans</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><summary type="html"><![CDATA[I came across some cheat sheets from the Microsoft Ajax library.  They were created by Millan Negovans.]]></summary></entry><entry><title type="html">LINQ Quick Starter</title><link href="https://www.josephguadagno.net/2008/01/04/linq-quick-starter/" rel="alternate" type="text/html" title="LINQ Quick Starter" /><published>2008-01-04T02:50:45-07:00</published><updated>2008-01-04T02:50:45-07:00</updated><id>https://www.josephguadagno.net/2008/01/04/linq-quick-starter</id><content type="html" xml:base="https://www.josephguadagno.net/2008/01/04/linq-quick-starter/"><![CDATA[<p><a href="https://www.packtpub.com/" target="_blank">Packt Publishing</a> recently contacted me to review their latest book, LINQ Quickly, so like any techie with three weeks off, I decided to catch up on some reading… Well actually I read this book on the flight to NY (5 hours).</p>

<p>Chapter 1 starts off with an brief introduction to <a href="https://msdn2.microsoft.com/en-us/netframework/aa904594.aspx" target="_blank">LINQ</a>, the rest of the chapter goes into detail of the new .NET 3.5 framework features created to enable LINQ. The next few chapters cover LINQ to … Objects, XML, SQL, and XSD. Chapter 6 describes how to use LINQ within you ADO.NET DataSets.</p>

<p>Chapter 7, which is worth the purchase price of the book covers all of the new Operators that are available to LINQ queries.  This makes the book worthy of purchasing a desktop reference guide for those just getting up to speed on LINQ.</p>

<p>What me to review your book on this blog, please <a href="/about-joseph-guadagno/">contact me</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[Packt Publishing recently contacted me to review their latest book, LINQ Quickly, so like any techie with three weeks off, I decided to catch up on some reading… Well actually I read this book on the flight to NY (5 hours).]]></summary></entry><entry><title type="html">Microsoft AJAX Library Essentials: Client-side ASP.NET AJAX 1.0 Explained</title><link href="https://www.josephguadagno.net/2008/01/04/microsoft-ajax-library-essentials-client-side-asp-net-ajax-1-0-explained/" rel="alternate" type="text/html" title="Microsoft AJAX Library Essentials: Client-side ASP.NET AJAX 1.0 Explained" /><published>2008-01-04T02:49:42-07:00</published><updated>2008-01-04T02:49:42-07:00</updated><id>https://www.josephguadagno.net/2008/01/04/microsoft-ajax-library-essentials-client-side-asp-net-ajax-1-0-explained</id><content type="html" xml:base="https://www.josephguadagno.net/2008/01/04/microsoft-ajax-library-essentials-client-side-asp-net-ajax-1-0-explained/"><![CDATA[<p>Contrary to what I thought when I originally got this book it is <strong>not</strong> related to the ASP.NET Ajax Control toolkit for which you find many articles and websites dedicated to, instead this book is about the library behind the scenes that makes Microsoft Ajax work.  This is a good thing because there are a limited number of locations to get some details about underlining JavaScript libraries.</p>

<p>Some of the pros of the book.</p>

<ul>
  <li>The author provides an in-depth break down of the code with step by step explanations of what the source code is doing.  This makes it easier to follow along.</li>
  <li>Many of the code samples are written out with comments, verbal explanations, and UML / class diagrams.</li>
  <li>The first three chapters are on Ajax development without any ties to Microsoft technologies.</li>
</ul>

<p>Some of the cons of the book.</p>

<ul>
  <li>A lot of large fonts were used, which makes it a bit unreadable at times.</li>
</ul>

<h2 id="on-to-the-book-content">On to the book content…</h2>

<p>Chapter 1 starts off with an introduction of why Ajax is needed and the components involved at a high level.  Chapter 2 goes into more detail of the foundation of Ajax covering the DOM, CSS, XMLHTTPRequest object.  This is a good overview of Ajax technology that is not tied to Microsoft technologies.</p>

<p>Chapter 3 contains an overview of adding some object-oriented programming (OOP) to JavaScript.</p>

<p>Chapter 4 recreates the examples Ajax application the was created in chapter 2 using the Microsoft Ajax library.  This is a good example to demonstrate the benefits of using the Microsoft Ajax library.</p>

<p>Chapter 5, which makes the book worth it weight in gold, goes into using Microsoft Ajax library to create “true” OOP JavaScript objects which work across many if not all modern browsers.</p>

<p>Chapter 6 goes into creating controls and components utilizing the library and Chapter 7 build on 6 with providing two component a timer and an enhanced text box control.</p>

<p>Chapter 8 goes into debugging Ajax application with either Firefox or Internet Explorer.</p>

<h3 id="overall">Overall</h3>

<p>The book is a good reference book to have if you want to learn more about the Microsoft Ajax library or how Ajax works.</p>

<p>What me to review your book on this blog , please <a href="/about-joseph-guadagno/">contact me</a>.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[Contrary to what I thought when I originally got this book it is not related to the ASP.NET Ajax Control toolkit for which you find many articles and websites dedicated to, instead this book is about the library behind the scenes that makes Microsoft Ajax work. This is a good thing because there are a limited number of locations to get some details about underlining JavaScript libraries.]]></summary></entry><entry><title type="html">Using the Search Sitemap Provider with the ASP.NET Futures release</title><link href="https://www.josephguadagno.net/2007/11/26/using-the-search-sitemap-provider-with-the-asp-net-futures-release" rel="alternate" type="text/html" title="Using the Search Sitemap Provider with the ASP.NET Futures release" /><published>2007-11-26T23:44:16-07:00</published><updated>2007-11-26T23:44:16-07:00</updated><id>https://www.josephguadagno.net/2007/11/26/using-the-search-sitemap-provider-with-the-asp-net-futures-release</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/26/using-the-search-sitemap-provider-with-the-asp-net-futures-release"><![CDATA[<h2 id="sitemaps">Sitemaps</h2>

<p>First off a quick introduction to what a Sitemap is, according to <a href="https://www.sitemaps.org" target="_blank">https://www.sitemaps.org</a>.</p>

<p>Sitemaps are an easy way for webmasters to inform search engines about pages on their sites that are available for crawling. In its simplest form, a Sitemap is an XML file that lists URLs for a site along with additional metadata about each URL (when it was last updated, how often it usually changes, and how important it is, relative to other URLs in the site) so that search engines can more intelligently crawl the site.</p>

<h2 id="aspnet-futures-release">ASP.NET Futures Release</h2>

<p>The ASP.NET futures release can be found at <a href="https://www.asp.net/downloads/futures/" title="https://www.asp.net/downloads/futures/" target="_blank">https://www.asp.net/downloads/futures/</a>. Just a note, as you will see on the futures site, the content in the futures release might not make it to the official ASP.NET Ajax release. The futures release contains functionality that the team is thinking about incorporating into the main ASP.NET release. So this means that you could play with the code and hope that the team incorporates it into ASP.NET Ajax.  Enough with the disclaimer.</p>

<p><a href="https://go.microsoft.com/fwlink/?LinkID=89147&amp;clcid=0x409" target="_blank"><strong>Download the ASP.NET Futures</strong>  (July 2007) Release</a></p>

<h2 id="getting-started-search-sitemap-provider">Getting Started Search Sitemap Provider</h2>

<p>The ASP.NET team created the <a href="https://quickstarts.asp.net/Futures/services/doc/searchsitemaps.aspx" target="_blank">SearchSitemapProvider</a> as part of the ASP.NET Futures (July 2007) release. The release contains an HttpHandler to process the calls, SearchSiteMaps.axd, which serves up the Sitemap and two providers, AspNetSiteMapSearchSiteMapProvider and DynamicDataSearchSiteMapProvider. The AspNetSiteMapSearchSiteMapProvider will generate a static Sitemap based on the ASP.NET sitemap. The DynamicDataSearchSiteMap provider provides a way to dynamically generate the Sitemap.</p>

<p>To enable Search Sitemaps in your ASP.NET you need to do the following.</p>

<ul>
  <li>Edit the web.config to add the searchSiteMap section</li>
</ul>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;microsoft.web.preview&gt;</span>
  <span class="nt">&lt;searchSiteMap</span> <span class="na">enabled=</span><span class="s">"true"</span><span class="nt">&gt;</span>
    <span class="nt">&lt;providers&gt;</span>
      <span class="nt">&lt;add</span> <span class="na">name=</span><span class="s">"Navigation"</span>
        <span class="na">type=</span><span class="s">"Microsoft.Web.Preview.Search.AspNetSiteMapSearchSiteMapProvider, Microsoft.Web.Preview"</span><span class="nt">/&gt;</span>
      <span class="nt">&lt;add</span> <span class="na">name=</span><span class="s">"Articles"</span>
        <span class="na">type=</span><span class="s">"JosephGuadagno.Web.BLL.SiteMap.ArticleSiteMapData, JosephGuadagno.Web.BLL"</span>
        <span class="na">targetUrl=</span><span class="s">""</span>
        <span class="na">targetUrlseparator=</span><span class="s">"/"</span>
        <span class="na">queryStringDataFormatString=</span><span class="s">"{0}"</span>
        <span class="na">queryStringDataFields=</span><span class="s">"Url"</span><span class="nt">/&gt;</span>
        <span class="nt">&lt;add</span> <span class="na">name=</span><span class="s">"Books"</span>
          <span class="na">type=</span><span class="s">"JosephGuadagno.Web.BLL.SiteMap.BookSiteMapData, JosephGuadagno.Web.BLL"</span>
          <span class="na">targetUrl=</span><span class="s">""</span>
          <span class="na">targetUrlseparator=</span><span class="s">"/"</span>
          <span class="na">queryStringDataFormatString=</span><span class="s">"{0}"</span>
          <span class="na">queryStringDataFields=</span><span class="s">"Url"</span><span class="nt">/&gt;</span>
        <span class="nt">&lt;add</span> <span class="na">name=</span><span class="s">"News"</span>
          <span class="na">type=</span><span class="s">"JosephGuadagno.Web.BLL.SiteMap.NewsSiteMapData, JosephGuadagno.Web.BLL"</span>
          <span class="na">targetUrl=</span><span class="s">""</span>
          <span class="na">targetUrlseparator=</span><span class="s">"/"</span>
          <span class="na">queryStringDataFormatString=</span><span class="s">"{0}"</span>
          <span class="na">queryStringDataFields=</span><span class="s">"Url"</span><span class="nt">/&gt;</span>
    <span class="nt">&lt;/providers&gt;</span>
  <span class="nt">&lt;/searchSiteMap&gt;</span>
<span class="nt">&lt;/microsoft.web.preview&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<ul>
  <li>Edit the web.config to add the httpHandlers section</li>
</ul>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;httpHandlers&gt;</span>
  <span class="nt">&lt;add</span> <span class="na">verb=</span><span class="s">"*"</span>
    <span class="na">path=</span><span class="s">"SearchSiteMaps.axd"</span>
    <span class="na">type=</span><span class="s">"Microsoft.Web.Preview.Search.SearchSiteMapHandler"</span>
    <span class="na">validate=</span><span class="s">"true"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/httpHandlers&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>A call to <code class="language-plaintext highlighter-rouge">https://www.josephguadagno.net/SearchSiteMaps.axd</code> will produce something like this.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;sitemapindex&gt;</span>
  <span class="nt">&lt;sitemap&gt;</span>
    <span class="nt">&lt;loc&gt;</span>
      https://www.josephguadagno.net/SearchSiteMaps.axd?sitemap=Navigation
    <span class="nt">&lt;/loc&gt;</span>
    <span class="nt">&lt;lastmod&gt;</span>2007-11-13T04:53:21.371Z<span class="nt">&lt;/lastmod&gt;</span>
  <span class="nt">&lt;/sitemap&gt;</span>
  <span class="nt">&lt;sitemap&gt;</span>
    <span class="nt">&lt;loc&gt;</span>
      https://www.josephguadagno.net/SearchSiteMaps.axd?sitemap=Articles
    <span class="nt">&lt;/loc&gt;</span>
    <span class="nt">&lt;lastmod&gt;</span>2007-11-13T04:53:21.371Z<span class="nt">&lt;/lastmod&gt;</span>
  <span class="nt">&lt;/sitemap&gt;</span>
  <span class="nt">&lt;sitemap&gt;</span>
    <span class="nt">&lt;loc&gt;</span>
      https://www.josephguadagno.net/SearchSiteMaps.axd?sitemap=Books
    <span class="nt">&lt;/loc&gt;</span>
    <span class="nt">&lt;lastmod&gt;</span>2007-11-13T04:53:21.371Z<span class="nt">&lt;/lastmod&gt;</span>
  <span class="nt">&lt;/sitemap&gt;</span>
  <span class="nt">&lt;sitemap&gt;</span>
    <span class="nt">&lt;loc&gt;</span>
      https://www.josephguadagno.net/SearchSiteMaps.axd?sitemap=News
    <span class="nt">&lt;/loc&gt;</span>
    <span class="nt">&lt;lastmod&gt;</span>2007-11-13T04:53:21.371Z<span class="nt">&lt;/lastmod&gt;</span>
  <span class="nt">&lt;/sitemap&gt;</span>
<span class="nt">&lt;/sitemapindex&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This will instruct the search engine that there are at least four sitemaps that are available.  Now the search engine will crawl these four URLs and get the additional URLs that are available. The four sitemaps point to the four providers that are in the web.config file.  The reason for four of them is that one of them is for the fixed navigation tied to the sitemap file, the other three use the dynamic site map provider, one for each type since the classes and data retrieval are different.</p>

<h2 id="using-the-search-sitemap-provider">Using the Search Sitemap Provider</h2>

<h3 id="aspnetsitemapsearchsitemapprovider">AspNetSiteMapSearchSiteMapProvider</h3>

<p>This provider is the easiest to use and only requires a valid Asp.Net sitemap file. Adding the following line to the searchSiteMap section of the web config file will instruct the provider to load the default asp.net site map.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;add</span> <span class="na">name=</span><span class="s">"Navigation"</span> 
  <span class="na">type=</span><span class="s">"Microsoft.Web.Preview.Search.AspNetSiteMapSearchSiteMapProvider, Microsoft.Web.Preview"</span><span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<h3 id="dynamicdatasearchsitemapprovider">DynamicDataSearchSiteMapProvider</h3>

<p>The DynamicDataSearchSiteMapProvider is used when you have dynamic content that you want to submit to the search engines.  Dynamic content is content that is generated on the fly or does not have a fixed URL. As you see in lines 5 - 19 of my searchSiteMap provider’s section, I use three different custom providers, Articles, Books and News.  These providers inherit from the DynamicDataSearchSiteMapProvider class which requires that you implement the DataQuery method which returns an IEnumerable interface. I choose to return a List&lt;SiteEntry&gt; objects.</p>

<p>The SiteEntry class supports the following properties (taken straight from the Asp.Net futures site):</p>

<ul>
  <li>The <code class="language-plaintext highlighter-rouge">targetUrl</code> property (required) specifies the URL of the page in the sitemap.</li>
  <li>The <code class="language-plaintext highlighter-rouge">targetUrlseparator</code> property (optional) specifies the separator between the URL and the data fields. The default is <code class="language-plaintext highlighter-rouge">?</code>. You can specify characters such as <code class="language-plaintext highlighter-rouge">#</code> or <code class="language-plaintext highlighter-rouge">/</code>.</li>
  <li>The <code class="language-plaintext highlighter-rouge">queryStringDataFormatString</code> property (optional) specifies how the data fields are formatted using syntax like that used by the <code class="language-plaintext highlighter-rouge">String.Format</code> method. If no format is specified, the provider auto-generates a default format string.</li>
  <li>The <code class="language-plaintext highlighter-rouge">queryStringDataFields</code> property (optional) specifies which columns you want to bind in <code class="language-plaintext highlighter-rouge">targetUrlFormatString</code>. If the property is not specified, the provider infers the list of column names from the collection returned by the <code class="language-plaintext highlighter-rouge">DataQuery</code> method.</li>
  <li>The <code class="language-plaintext highlighter-rouge">lastModifiedDataField</code> property (optional) specifies the column that contains information about the last time the sitemap was modified. This date should be in W3C DateTime format, which allows you to omit the time portion and provide the date in the format YYYY-MM-DD. If the property is not specified, the provider attempts to read a property named <code class="language-plaintext highlighter-rouge">SiteMapLastModified</code>.</li>
  <li>The <code class="language-plaintext highlighter-rouge">changeFrequencyDataField</code> property (optional) specifies how frequently the page is likely to change. Valid values are: always, hourly, daily, weekly, monthly, yearly, never. If the property is not specified, the provider attempts to read a property named <code class="language-plaintext highlighter-rouge">SiteMapChangeFrequency</code>.</li>
  <li>The <code class="language-plaintext highlighter-rouge">priorityDataField</code> property (optional) specifies the priority of this URL relative to other URLs on your site. Valid values range from 0.0 to 1.0. If the property is not specified, the provider attempts to read a property named <code class="language-plaintext highlighter-rouge">SiteMapPriority</code>.</li>
  <li>Set the <code class="language-plaintext highlighter-rouge">pathInfoFormat</code> property (optional) to <code class="language-plaintext highlighter-rouge">true</code> if you want to use only the value in the URL (for example, <code class="language-plaintext highlighter-rouge">https://site/page.aspx/1</code>)</li>
</ul>

<p>These properties are used in conjunction with your properties that you want to display in the sitemap. For my search sitemap I use the following fields:</p>

<table>
  <thead>
    <tr>
      <th>Url</th>
      <th>Custom Field</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>SiteMapLastModified</td>
      <td>specifies the last time the content changed</td>
    </tr>
    <tr>
      <td>SiteMapChangeFrequency</td>
      <td>specifies how often this page is expected to change</td>
    </tr>
    <tr>
      <td>SiteMapPriority</td>
      <td>specifies the priority of this Url verses others</td>
    </tr>
    <tr>
      <td>targetUrl</td>
      <td>specifies the url that will be used</td>
    </tr>
    <tr>
      <td>targetUrlSeparator</td>
      <td>specifies the separator between the query string</td>
    </tr>
    <tr>
      <td>queryStringDataFormatString</td>
      <td>specifies how the sitemap provider will format the url</td>
    </tr>
    <tr>
      <td>queryStringDataFields</td>
      <td>specifies what properties from the SiteEntry object you will use to pass to the queryStringDataFormatString (similar to the <code class="language-plaintext highlighter-rouge">String.Format</code>)</td>
    </tr>
  </tbody>
</table>

<p>The <code class="language-plaintext highlighter-rouge">Url</code>, <code class="language-plaintext highlighter-rouge">SiteMapLastModified</code>, <code class="language-plaintext highlighter-rouge">SiteMapChangeFrequency</code>, <code class="language-plaintext highlighter-rouge">SiteMapPriority</code> properties are set in code and the <code class="language-plaintext highlighter-rouge">targetUrl</code>, <code class="language-plaintext highlighter-rouge">targetUrlseparator</code>, <code class="language-plaintext highlighter-rouge">queryStringDataFormatString</code>, and <code class="language-plaintext highlighter-rouge">queryStringDataFields</code> are set in the web config.</p>

<p>So what do all of the there properties mean and do. Well like I mentioned above the DynamicDataSearchSiteMapProvider expects a DataQuery method to be implemented.  This method is expected to return an <code class="language-plaintext highlighter-rouge">IEnumerable</code> (collection) of objects. In my case, I return a List&lt;SiteEntry&gt; objects.  My SiteEntry class looks like this.</p>

<p>TODO: Find Image “image2.png”</p>

<p>The ArticleSiteMapData provider looks like this</p>

<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
</pre></td><td class="rouge-code"><pre><span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// A SiteMap provider for the Article page.</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="c1">/// &lt;summary&gt;</span>
<span class="c1">/// A SiteMap provider for the Article page.</span>
<span class="c1">/// &lt;/summary&gt;</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">ArticleSiteMapData</span><span class="p">:</span> <span class="n">DynamicDataSearchSiteMapProvider</span>
<span class="p">{</span>
  <span class="k">public</span> <span class="nf">ArticleSiteMapData</span><span class="p">()</span> <span class="p">{}</span>

  <span class="k">public</span> <span class="k">override</span> <span class="n">IEnumerable</span> <span class="nf">DataQuery</span><span class="p">()</span>
  <span class="p">{</span>
    <span class="c1">// Holds a list of the ArticleEntry's</span>
    <span class="n">List</span><span class="p">&lt;</span><span class="n">SiteEntry</span><span class="p">&gt;</span> <span class="n">list</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p">&lt;</span><span class="n">SiteEntry</span><span class="p">&gt;();</span>
    <span class="n">TList</span><span class="p">&lt;</span><span class="n">Article</span><span class="p">&gt;</span> <span class="n">articles</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TList</span><span class="p">&lt;</span><span class="n">Article</span><span class="p">&gt;();</span>

    <span class="n">articles</span> <span class="p">=</span> <span class="n">DataRepository</span><span class="p">.</span><span class="n">ArticleProvider</span><span class="p">.</span><span class="nf">GetAll</span><span class="p">();</span>
    <span class="k">foreach</span> <span class="p">(</span><span class="n">Article</span> <span class="n">article</span> <span class="k">in</span> <span class="n">articles</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="kt">string</span> <span class="n">url</span> <span class="p">=</span> <span class="n">BLL</span><span class="p">.</span><span class="n">URLRewrite</span><span class="p">.</span><span class="nf">GetArticleRewrite</span><span class="p">(</span><span class="n">article</span><span class="p">);</span>
      <span class="n">SiteEntry</span> <span class="n">siteEntry</span> <span class="p">=</span>
        <span class="k">new</span> <span class="nf">SiteEntry</span><span class="p">(</span><span class="n">url</span><span class="p">,</span>
          <span class="n">article</span><span class="p">.</span><span class="n">AddedOn</span><span class="p">,</span>
          <span class="n">SiteEntry</span><span class="p">.</span><span class="n">ChangeFrequency</span><span class="p">.</span><span class="n">Daily</span><span class="p">,</span>
          <span class="s">"0.5"</span><span class="p">);</span>
      <span class="n">list</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">siteEntry</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">articles</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">list</span><span class="p">.</span><span class="nf">ToArray</span><span class="p">();</span>
  <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Lines 14 - 21 you would replace with your code get your dynamic data.  Lines 19 and 20 is where the SiteEntry object is created and added to the List.</p>

<p>So how does the <code class="language-plaintext highlighter-rouge">List&lt;SiteEntry&gt;</code> tie into the provider configuration?  The answer is in the attributes/properties that are defined for provider. For this sample we have <code class="language-plaintext highlighter-rouge">targetUrl =""</code>, <code class="language-plaintext highlighter-rouge">targetUrlSeparator="/"</code>, <code class="language-plaintext highlighter-rouge">queryStringDataFormatString="{0}"</code> and <code class="language-plaintext highlighter-rouge">queryStringDataFields="Url"</code> this tells the provide to build a string with the base of targetUrl, then add <code class="language-plaintext highlighter-rouge">targetUrlSeparator</code>, followed by calling <code class="language-plaintext highlighter-rouge">string.forma</code>t using <code class="language-plaintext highlighter-rouge">queryStringDataFormatString</code> with the parameters equal to the value of the properties listed in <code class="language-plaintext highlighter-rouge">queryStringDataFields</code>.  In other words the url will look like this.</p>

<p><code class="language-plaintext highlighter-rouge">https://www.yourdomain.com/&lt;value of url field&gt;</code></p>

<p>In my site, I generate the Url in code so that it will be consistent between different pages.  A “real” world example might look like this.</p>

<p>Properties: <code class="language-plaintext highlighter-rouge">targetUrl ="BookReviews"</code>, <code class="language-plaintext highlighter-rouge">targetUrlSeparator="/"</code>, <code class="language-plaintext highlighter-rouge">queryStringDataFormatString="{0}/{1}.aspx"</code><code class="language-plaintext highlighter-rouge"> and queryStringDataFields="Id, Title"</code> which would generate a url similar to this.</p>

<p><code class="language-plaintext highlighter-rouge">https://www.yourdomain.com/1/title.aspx</code></p>

<p>Notice that I had two field in the <code class="language-plaintext highlighter-rouge">queryStringDataFields</code> property.  You can list as many as you want, they just need to be comma separated. This means that the SiteEntry object needs to have at least the two properties of <code class="language-plaintext highlighter-rouge">Id</code> and <code class="language-plaintext highlighter-rouge">Title</code>.</p>

<h2 id="for-more-information">For more information</h2>

<p>That’s about it.  I was a lot to read and digest but hopefully useful. If you have any questions, please feel free to contact me or leave a comment or you can discuss the Sitemap provider in the ASP.net <a href="https://forums.asp.net/1127/ShowForum.aspx" target="_blank">forums</a>.</p>

<h2 id="search-engines-were-you-can-submit-a-feed">Search Engines were you can submit a feed</h2>

<p>Here some search engine urls where you can submit you site maps.</p>

<p>Google: <a href="https://www.google.com/webmasters/tools/siteoverview" title="https://www.google.com/webmasters/tools/siteoverview" target="_blank">https://www.google.com/webmasters/tools/siteoverview</a></p>

<p>Yahoo: <a href="https://siteexplorer.search.yahoo.com" title="https://siteexplorer.search.yahoo.com" target="_blank">https://siteexplorer.search.yahoo.com</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="ASP.NET" /><category term="Sitemap" /><category term="Search" /><category term="SEO" /><summary type="html"><![CDATA[Sitemaps]]></summary></entry><entry><title type="html">ASP.NET 2.0 Web Parts in Action: Building Dynamic Web Portals</title><link href="https://www.josephguadagno.net/2007/11/23/asp-net-2-0-web-parts-in-action-building-dynamic-web-portals/" rel="alternate" type="text/html" title="ASP.NET 2.0 Web Parts in Action: Building Dynamic Web Portals" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/asp-net-2-0-web-parts-in-action-building-dynamic-web-portals</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/asp-net-2-0-web-parts-in-action-building-dynamic-web-portals/"><![CDATA[<p>This book provides an in-depth look at using, implementing, customizing and developing your own web parts.</p>

<p>Topics include:</p>

<ul>
  <li>Web Part Connections</li>
  <li>Zones and how web parts behave inside them</li>
  <li>Tips &amp; Tricks to get web parts to behave like they look in SharePoint</li>
  <li>Page Life cycle with web parts</li>
  <li>AJAX and web parts (little light on that topic)</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book provides an in-depth look at using, implementing, customizing and developing your own web parts.]]></summary></entry><entry><title type="html">ASP.NET 2.0 Website Programming: Problem – Design – Solution (Programmer to Programmer)</title><link href="https://www.josephguadagno.net/2007/11/23/asp-net-2-0-website-programming-problem-design-solution-programmer-to-programmer/" rel="alternate" type="text/html" title="ASP.NET 2.0 Website Programming: Problem – Design – Solution (Programmer to Programmer)" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/asp-net-2-0-website-programming-problem-design-solution-programmer-to-programmer</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/asp-net-2-0-website-programming-problem-design-solution-programmer-to-programmer/"><![CDATA[<p>This is a <strong>GREAT</strong> book for anyone getting starting in ASP.NET. It covers everything you need to create an ASP.NET web site. Hey, the first version of this website was built based on the content of this book.<br />
The topics covered:</p>

<ul>
  <li>Account registration, personalization and theming</li>
  <li>News and events, organized into categories</li>
  <li>Opinion polls</li>
  <li>Newsletter</li>
  <li>Forums</li>
  <li>E-commerce store with shopping cart and order management</li>
  <li>Localization</li>
</ul>

<p>Administration of a site will be covered including:</p>

<ul>
  <li>Full online back-end administrative section, to manage practically all data from an intuitive user interface</li>
  <li>Site deployment</li>
</ul>

<p>In building these site features, you’ll learn these new ASP.NET 2.0 features:</p>

<ul>
  <li>Master pages</li>
  <li>Theming</li>
  <li>Personalization &amp; Web parts</li>
  <li>Membership &amp; Profile modules</li>
  <li>Personalization</li>
  <li>The new server-side UI controls such as GridView, DetailsView, FormView, Wizard, MultiView, the new xxxDataSource and navigation controls, among others.</li>
  <li>The new compilation mode and deployment modes</li>
  <li>The new framework for instrumenting the site, as well as handling &amp; logging exceptions</li>
  <li>The new ADO.NET 2.0 features (e.g. caching with database dependency)</li>
  <li>The new classes for easy distributed transactions Management</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This is a GREAT book for anyone getting starting in ASP.NET. It covers everything you need to create an ASP.NET web site. Hey, the first version of this website was built based on the content of this book. The topics covered:]]></summary></entry><entry><title type="html">C# Essentials</title><link href="https://www.josephguadagno.net/2007/11/23/c-essentials/" rel="alternate" type="text/html" title="C# Essentials" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/c-essentials</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/c-essentials/"><![CDATA[<p>The book itself is quite thin but covers enough of C# to make it useful for an introduction and to see the potential for the .NET framework.</p>

<p>The topics covered:</p>

<ul>
  <li>Introduction and tutorial for C#</li>
  <li>The Common Language Runtime (CLR)</li>
  <li>A minimal program in C#</li>
  <li>Identifiers and variables</li>
  <li>Inheritance</li>
  <li>Classes and interfaces</li>
  <li>Arrays</li>
  <li>Enums</li>
  <li>Delegates</li>
  <li>Events</li>
  <li>Exception handling</li>
  <li>Attributes</li>
  <li>Unsafe code</li>
  <li>Programming with the built-in .NET Framework classes</li>
  <li>Math and string classes</li>
  <li>Collections</li>
  <li>Support for regular expressions</li>
  <li>I/O and networking classes</li>
  <li>Threading</li>
  <li>.NET assemblies and deployment issues</li>
  <li>Reflection</li>
  <li>Interoperability with COM and .NET</li>
  <li>Overview of .NET packages for common programming tasks (including collections, Web services, XML, and Web-based applications)</li>
  <li>Essential .NET tools</li>
  <li>References to C# keywords</li>
  <li>References and format specifiers</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[The book itself is quite thin but covers enough of C# to make it useful for an introduction and to see the potential for the .NET framework.]]></summary></entry><entry><title type="html">Essential Windows Presentation Foundation</title><link href="https://www.josephguadagno.net/2007/11/23/essential-windows-presentation-foundation/" rel="alternate" type="text/html" title="Essential Windows Presentation Foundation" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/essential-windows-presentation-foundation</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/essential-windows-presentation-foundation/"><![CDATA[<p>If you want to find out how Windows Presentation Foundation (WPF) works and why it was created, then this book is for you.<br />
Topics Covered:</p>

<ul>
  <li>WPF components and architecture</li>
  <li>Key WPF design decisions–and why they matter</li>
  <li>XAML markup language</li>
  <li>Controls</li>
  <li>Layouts</li>
  <li>Visuals and media, including 2D, 3D, video, and animation</li>
  <li>Data integration</li>
  <li>Actions</li>
  <li>Styles</li>
  <li>WPF Base Services</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[If you want to find out how Windows Presentation Foundation (WPF) works and why it was created, then this book is for you. Topics Covered:]]></summary></entry><entry><title type="html">Inside Windows Communication Foundation</title><link href="https://www.josephguadagno.net/2007/11/23/inside-windows-communication-foundation/" rel="alternate" type="text/html" title="Inside Windows Communication Foundation" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/inside-windows-communication-foundation</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/inside-windows-communication-foundation/"><![CDATA[<p>As with most of the content from <a href="https://www.wintellect.com" target="_blank">Wintellect</a>, this book is an in-depth look at the Windows Communication Foundation. There is plenty of code and examples look at and use.<br />
Topics Covered:</p>

<ul>
  <li>Service Orientation</li>
  <li>WCF 101</li>
  <li>The Channel Layer: Messages</li>
  <li>The Channel Layer: Channels</li>
  <li>The Channel Layer: Channel Managers</li>
  <li>The ServiceModel Layer: Bindings</li>
  <li>The ServiceModel Layer: Contracts</li>
  <li>The ServiceModel Layer: Dispatchers and Clients</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[As with most of the content from Wintellect, this book is an in-depth look at the Windows Communication Foundation. There is plenty of code and examples look at and use. Topics Covered:]]></summary></entry><entry><title type="html">Introducing Microsoft ASP.NET AJAX</title><link href="https://www.josephguadagno.net/2007/11/23/introducing-microsoft-asp-net-ajax/" rel="alternate" type="text/html" title="Introducing Microsoft ASP.NET AJAX" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/introducing-microsoft-asp-net-ajax</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/introducing-microsoft-asp-net-ajax/"><![CDATA[<p>So far I think every book or article I have read by Dino Esposito has been great, this is no exception!<br />
This book is broken up into three parts:</p>

<ul>
  <li>The building blocks of Ajax</li>
  <li>Adding Ajax to an existing site</li>
  <li>Client-Centric Development</li>
</ul>

<p>Oh yeah, and some bonus content at the end!<br />
Overall this book is an excellent starting place for getting into Ajax development or to have a reference.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[So far I think every book or article I have read by Dino Esposito has been great, this is no exception! This book is broken up into three parts:]]></summary></entry><entry><title type="html">Introducing Microsoft LINQ</title><link href="https://www.josephguadagno.net/2007/11/23/introducing-microsoft-linq/" rel="alternate" type="text/html" title="Introducing Microsoft LINQ" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/introducing-microsoft-linq</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/introducing-microsoft-linq/"><![CDATA[<p>This book is built on the beta of version 3.5 of the .NET framework. Since LINQ is a subset of the language enhancements made to .NET 3.5, the author chose to cover some of them also like Local Type Inference, kinda cool but I think it will lead to sloppy code. Overall I would say go out and get the book since .NET 3.5 is here. Topics Include:</p>

<ul>
  <li>LINQ Introduction</li>
  <li>C# Language Features</li>
  <li>Visual Basic 9 Language Features</li>
  <li>LINQ Syntax Fundamentals</li>
  <li>LINQ to SQL</li>
  <li>LINQ to XML</li>
  <li>ADO.NET Entity Framework</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book is built on the beta of version 3.5 of the .NET framework. Since LINQ is a subset of the language enhancements made to .NET 3.5, the author chose to cover some of them also like Local Type Inference, kinda cool but I think it will lead to sloppy code. Overall I would say go out and get the book since .NET 3.5 is here. Topics Include:]]></summary></entry><entry><title type="html">Microsoft® SharePoint® 2007 Development Unleashed</title><link href="https://www.josephguadagno.net/2007/11/23/microsoft-sharepoint-2007-development-unleashed/" rel="alternate" type="text/html" title="Microsoft® SharePoint® 2007 Development Unleashed" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/microsoft-sharepoint-2007-development-unleashed</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/microsoft-sharepoint-2007-development-unleashed/"><![CDATA[<p>When they put the word <strong>Unleashed</strong> in this title they were not kidding. This book has everything (or at least everything I needed) as it pertains to SharePoint development. Most books concentrate on building SharePoint sites, creating lists, setting permissions but not this book. This book looks at how to work with SharePoint through it’s .NET objects and Web Services. A <strong>must</strong> if you are developing against SharePoint. Topics Covered:</p>

<ul>
  <li>Use CAML, SharePoint’s XML-based language for defining content, manipulating searches, and more</li>
  <li>Work with the SharePoint Object Model</li>
  <li>Build reusable packages for easy deployment to SharePoint server farms</li>
  <li>Program SharePoint webs, sites, document libraries, and files</li>
  <li>Leverage SharePoint 2007’s improved lists and new list events</li>
  <li>Manipulate and query meetings and Meeting Workspaces</li>
  <li>Integrate external business data into SharePoint applications</li>
  <li>Construct business workflows for enterprise content management and other applications</li>
  <li>Program ASP.NET-based SharePoint Web Parts, from the basics to state-of-the-art techniques</li>
  <li>Create Web Parts that can provide and consume data through connections</li>
  <li>Use SharePoint 2007’s built-in Web services for managing document and Meeting Workspaces, imaging, and lists</li>
  <li>Centralize spreadsheet storage and management with Excel Services</li>
  <li>Manage user profiles and enhance application security</li>
  <li>Debug and deploy SharePoint 2007 applications</li>
  <li>Work with Records Repositories and metadata</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[When they put the word Unleashed in this title they were not kidding. This book has everything (or at least everything I needed) as it pertains to SharePoint development. Most books concentrate on building SharePoint sites, creating lists, setting permissions but not this book. This book looks at how to work with SharePoint through it’s .NET objects and Web Services. A must if you are developing against SharePoint. Topics Covered:]]></summary></entry><entry><title type="html">Pro ASP.NET 2.0 in C# 2005</title><link href="https://www.josephguadagno.net/2007/11/23/pro-asp-net-2-0-in-c-2005/" rel="alternate" type="text/html" title="Pro ASP.NET 2.0 in C# 2005" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/pro-asp-net-2-0-in-c-2005</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/pro-asp-net-2-0-in-c-2005/"><![CDATA[<p>This book is a good for those of you who are experienced developers waiting to get into ASP.NET.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book is a good for those of you who are experienced developers waiting to get into ASP.NET.]]></summary></entry><entry><title type="html">Programming ASP.NET AJAX: Build rich, Web 2.0-style UI with ASP.NET AJAX</title><link href="https://www.josephguadagno.net/2007/11/23/programming-asp-net-ajax-build-rich-web-2-0-style-ui-with-asp-net-ajax/" rel="alternate" type="text/html" title="Programming ASP.NET AJAX: Build rich, Web 2.0-style UI with ASP.NET AJAX" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/programming-asp-net-ajax-build-rich-web-2-0-style-ui-with-asp-net-ajax</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/programming-asp-net-ajax-build-rich-web-2-0-style-ui-with-asp-net-ajax/"><![CDATA[<p>Unfortunately with most books on web technologies they become outdated when they hit the press, this book is one of them.  It, however, contains so good reference material.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[Unfortunately with most books on web technologies they become outdated when they hit the press, this book is one of them. It, however, contains so good reference material.]]></summary></entry><entry><title type="html">Programming .NET Components, 2nd Edition</title><link href="https://www.josephguadagno.net/2007/11/23/programming-net-components-2nd-edition/" rel="alternate" type="text/html" title="Programming .NET Components, 2nd Edition" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/programming-net-components-2nd-edition</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/programming-net-components-2nd-edition/"><![CDATA[<p>This book is a must for those that want to go beyond the “Hello World” applications. It covers many of the “advanced” topics in building .NET applications. The chapter on Generics is well worth the price of the book. This is a ** must-have**. Topics Covered:</p>

<ul>
  <li>Introducing Component-Oriented Programming</li>
  <li>.NET Component-Oriented Programming Essentials</li>
  <li>Interface-Based Programming</li>
  <li>Lifecycle Management</li>
  <li>Versioning</li>
  <li>Events</li>
  <li>Asynchronous Calls</li>
  <li>Multithreading and Concurrency Management</li>
  <li>Serialization and Persistence</li>
  <li>Remoting</li>
  <li>Context and Interception</li>
  <li>Security</li>
  <li>Interface-Based Web Services</li>
  <li>Unifying Windows Forms and ASP.NET Security</li>
  <li>Reflection and Attributes</li>
  <li>Generics</li>
  <li>C# Coding Standards</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book is a must for those that want to go beyond the “Hello World” applications. It covers many of the “advanced” topics in building .NET applications. The chapter on Generics is well worth the price of the book. This is a ** must-have**. Topics Covered:]]></summary></entry><entry><title type="html">Windows Communication Foundation Unleashed</title><link href="https://www.josephguadagno.net/2007/11/23/windows-communication-foundation-unleashed/" rel="alternate" type="text/html" title="Windows Communication Foundation Unleashed" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/windows-communication-foundation-unleashed</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/windows-communication-foundation-unleashed/"><![CDATA[<p>Similar to other books in the “Unleashed” series, this book goes all out to explain Windows Communication Foundation in an easy to understand way. This book also goes beyond the basics of Windows Communication Foundation but goes in-depth into the technology including security and service design. This is a <strong>must</strong> buy for WCF developers. Sections of the book:</p>

<ul>
  <li>Introducing the Windows Communication Foundation</li>
  <li>Introducing the Windows Workflow Foundation</li>
  <li>Security</li>
  <li>Integration and Interoperability</li>
  <li>Extending the Windows Communication Foundation</li>
  <li>Special Cases</li>
  <li>The Lifecycle of Windows Communication Foundation Applications</li>
  <li>Guidance</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[Similar to other books in the “Unleashed” series, this book goes all out to explain Windows Communication Foundation in an easy to understand way. This book also goes beyond the basics of Windows Communication Foundation but goes in-depth into the technology including security and service design. This is a must buy for WCF developers. Sections of the book:]]></summary></entry><entry><title type="html">Windows Presentation Foundation Unleashed</title><link href="https://www.josephguadagno.net/2007/11/23/windows-presentation-foundation-unleashed/" rel="alternate" type="text/html" title="Windows Presentation Foundation Unleashed" /><published>2007-11-22T18:00:00-07:00</published><updated>2007-11-22T18:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/23/windows-presentation-foundation-unleashed</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/23/windows-presentation-foundation-unleashed/"><![CDATA[<p>Just like the other Unleashed series of books, this one goes all out to explain Windows Presentation Foundation. The book actually has color examples which helps to illustrate some of the points. A <strong>must</strong> for a developer.<br />
Topics covered:</p>

<ul>
  <li>Covers everything you need to know about Extensible Application Markup Language (XAML)</li>
  <li>Examines the WPF feature areas in incredible depth: controls, layout, resources, data binding, styling, graphics, animation, and more</li>
  <li>Features a chapter on 3D graphics by Daniel Lehenbauer, the lead developer responsible for WPF 3D</li>
  <li>Delves into non-mainstream topics: speech, audio/video, documents, bitmap effects, and more</li>
  <li>Shows how to create popular UI elements, such as features introduced in the 2007 Microsoft Office System: Galleries, ScreenTips, custom control layouts, and more</li>
  <li>Demonstrates how to create sophisticated UI mechanisms, such as Visual Studio-like collapsible/dockable panes</li>
  <li>Explains how to develop and deploy all types of applications, including navigation-based applications, applications hosted in a Web browser, and applications with great-looking non-rectangular windows</li>
  <li>Explains how to create first-class custom controls for WPF</li>
  <li>Demonstrates how to create hybrid WPF software that leverages Windows Forms, ActiveX, or other non-WPF technologies</li>
  <li>Explains how to exploit new Windows Vista features in WPF applications</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[Just like the other Unleashed series of books, this one goes all out to explain Windows Presentation Foundation. The book actually has color examples which helps to illustrate some of the points. A must for a developer. Topics covered:]]></summary></entry><entry><title type="html">Introducing Microsoft ASP.NET AJAX</title><link href="https://www.josephguadagno.net/2007/11/09/introducing-microsoft-asp-net-ajax-2/" rel="alternate" type="text/html" title="Introducing Microsoft ASP.NET AJAX" /><published>2007-11-09T09:00:00-07:00</published><updated>2007-11-09T09:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/09/introducing-microsoft-asp-net-ajax-2</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/09/introducing-microsoft-asp-net-ajax-2/"><![CDATA[<p>A pretty good introduction to the Ajax framework provided by Microsoft <a href="https://www.asp.net/ajax" target="_blank">https://www.asp.net/ajax</a>. Unfortunately, that most of the content now you can find on the ASP.NET site.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[A pretty good introduction to the Ajax framework provided by Microsoft https://www.asp.net/ajax. Unfortunately, that most of the content now you can find on the ASP.NET site.]]></summary></entry><entry><title type="html">Microsoft SQL Server 2005 Stored Procedure Programming in T-SQL &amp;amp;amp; .NET</title><link href="https://www.josephguadagno.net/2007/11/09/microsoft-sql-server-2005-stored-procedure-programming-in-t-sql-amp-net/" rel="alternate" type="text/html" title="Microsoft SQL Server 2005 Stored Procedure Programming in T-SQL &amp;amp;amp; .NET" /><published>2007-11-09T09:00:00-07:00</published><updated>2007-11-09T09:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/09/microsoft-sql-server-2005-stored-procedure-programming-in-t-sql-amp-net</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/09/microsoft-sql-server-2005-stored-procedure-programming-in-t-sql-amp-net/"><![CDATA[<p>I primarily read this book to see what specifically SQL Server 2005 has as far as CLR integration is concerned. This book was a good overview of all aspects of SQL Server development. It covered T-SQL stored procedures and general SQL queries. From a CLR perspective, it covered the following:</p>

<ul>
  <li>CLR development (about 12 pages)</li>
  <li>CLR stored procedures (about 20)</li>
  <li>Functions and stored procedures (about 30)</li>
  <li>Advanced topics (about 40)</li>
</ul>

<p>I guess the point is that there is not much extra stuff you need to know to utilize the .NET CLR in SQL Server 2005.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[I primarily read this book to see what specifically SQL Server 2005 has as far as CLR integration is concerned. This book was a good overview of all aspects of SQL Server development. It covered T-SQL stored procedures and general SQL queries. From a CLR perspective, it covered the following:]]></summary></entry><entry><title type="html">Professional Web Parts and Custom Controls with ASP.NET 2.0</title><link href="https://www.josephguadagno.net/2007/11/09/professional-web-parts-and-custom-controls-with-asp-net-2-0/" rel="alternate" type="text/html" title="Professional Web Parts and Custom Controls with ASP.NET 2.0" /><published>2007-11-09T09:00:00-07:00</published><updated>2007-11-09T09:00:00-07:00</updated><id>https://www.josephguadagno.net/2007/11/09/professional-web-parts-and-custom-controls-with-asp-net-2-0</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/09/professional-web-parts-and-custom-controls-with-asp-net-2-0/"><![CDATA[<p>As the name of the book suggest it covers a combination of WebParts and Custom Controls.  Unfortunately, the author/book jumps back and forth between the two topics.  This book assumes that you are well versed in WebForms and are looking to move the next step in development.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[As the name of the book suggest it covers a combination of WebParts and Custom Controls. Unfortunately, the author/book jumps back and forth between the two topics. This book assumes that you are well versed in WebForms and are looking to move the next step in development.]]></summary></entry><entry><title type="html">Database Drivers not provided by Microsoft</title><link href="https://www.josephguadagno.net/2007/11/06/database-drivers-not-provided-by-microsoft/" rel="alternate" type="text/html" title="Database Drivers not provided by Microsoft" /><published>2007-11-06T00:04:33-07:00</published><updated>2007-11-06T00:04:33-07:00</updated><id>https://www.josephguadagno.net/2007/11/06/database-drivers-not-provided-by-microsoft</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/06/database-drivers-not-provided-by-microsoft/"><![CDATA[<h3 id="oracle">Oracle</h3>

<p>Oracle’s .NET center can be found here: <a href="https://www.oracle.com/technology/tech/dotnet/index.html" target="_blank">https://www.oracle.com/technology/tech/dotnet/index.html</a></p>

<p>Oracle 64bit ODAC drivers can be found here: <a href="https://www.oracle.com/technology/software/tech/windows/odpnet/64-bit/index.html" target="_blank">https://www.oracle.com/technology/software/tech/windows/odpnet/64-bit/index.html</a></p>

<p>Very helpful for Oracle development and integrates directing into Visual Studio. Oracle Developer Tools for Visual Studio .NET: <a href="https://www.oracle.com/technology/tech/dotnet/tools/index.html" title="https://www.oracle.com/technology/tech/dotnet/tools/index.html" target="_blank">https://www.oracle.com/technology/tech/dotnet/tools/index.html</a></p>

<p>Oracle ASP.NET Data Providers: <a href="https://www.oracle.com/technology/tech/dotnet/aspnet/" title="https://www.oracle.com/technology/tech/dotnet/aspnet/" target="_blank">https://www.oracle.com/technology/tech/dotnet/aspnet/</a></p>

<h3 id="mysql">MySQL</h3>

<p>MySQL ADO.NET Driver (Connector/NET): <a href="https://dev.mysql.com/downloads/connector/net/" title="https://dev.mysql.com/downloads/connector/net/" target="_blank">https://dev.mysql.com/downloads/connector/net/</a></p>

<p>Step by Step Guide for using MySQL with ASP.NET: <a href="https://www.15seconds.com/issue/050407.htm" target="_blank">https://www.15seconds.com/issue/050407.htm</a></p>

<p>MySQLRoleProvider: <a href="https://www.codeproject.com/aspnet/MySQLsuite.asp" target="_blank">https://www.codeproject.com/aspnet/MySQLsuite.asp</a></p>

<h3 id="sqlite-3">SQLite 3</h3>

<p>SQlite3 .NET Provider: <a href="https://sourceforge.net/projects/sqlite-dotnet2" title="https://sourceforge.net/projects/sqlite-dotnet2" target="_blank">https://sourceforge.net/projects/sqlite-dotnet2</a></p>

<p>SQLite ASP.NET Membership Provider: <a href="https://www.codeproject.com/useritems/SQLiteProviders.asp" title="https://www.codeproject.com/useritems/SQLiteProviders.asp" target="_blank">https://www.codeproject.com/useritems/SQLiteProviders.asp</a></p>

<p>SQLite 2007 Pro Enterprise Manager is a free GUI (Graphical User Interface) for working with SQLite database. I have used its predecessor and found it quite helpful. <a href="https://www.osenxpsuite.net/?xp=3" title="https://www.osenxpsuite.net/?xp=3" target="_blank">https://www.osenxpsuite.net/?xp=3</a></p>

<h3 id="nhibernate">NHibernate</h3>

<p>Sort of a repost but I wanted to group them together</p>

<p>NHibernate Data Layer Generation :<a href="https://sourceforge.net/projects/nhib-gen/" target="_blank">https://sourceforge.net/projects/nhib-gen/</a></p>

<p>This project is hosted on SourceForge.  This project requires <a href="https://www.codesmithtools.com/" target="_blank">CodeSmith</a> to generate the code.  The project generates, C# classes for the business layer, data layer, and DataSources.  In addition, these templates will generate some based Insert, Update, Delete ASP.NET pages.</p>

<p>NHibernateDataSource: <a href="https://www.codeproject.com/useritems/NHibernateDataSource.asp" target="_blank">https://www.codeproject.com/useritems/NHibernateDataSource.asp</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term=".NET" /><category term="ADO.NET" /><category term="Oracle" /><category term="MySQL" /><category term="SQLite" /><category term="NHibernate" /><category term="Database" /><summary type="html"><![CDATA[Oracle]]></summary></entry><entry><title type="html">Desert Code Camp: Using AjaxPro.Net</title><link href="https://www.josephguadagno.net/2007/11/06/desert-code-camp-using-ajaxpro-net/" rel="alternate" type="text/html" title="Desert Code Camp: Using AjaxPro.Net" /><published>2007-11-06T00:04:33-07:00</published><updated>2007-11-06T00:04:33-07:00</updated><id>https://www.josephguadagno.net/2007/11/06/desert-code-camp-using-ajaxpro-net</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/06/desert-code-camp-using-ajaxpro-net/"><![CDATA[<p>Download the PowerPoint® slide at <a href="/assets/downloads/Desert-Code-Camp.ppt">Desert Code Camp.ppt</a></p>

<p>Download the source code in C# at <a href="/assets/downloads/Contacts.zip">contacts.zip</a></p>

<p>Download the source code in VB at <a href="/assets/downloads/ContactsVB.zip">contactsvb.zip</a></p>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Miscellaneous" /><summary type="html"><![CDATA[Download the PowerPoint® slide at Desert Code Camp.ppt]]></summary></entry><entry><title type="html">How do I format my GridView bound column differently?</title><link href="https://www.josephguadagno.net/2007/11/06/how-do-i-format-my-gridview-bound-column-differently/" rel="alternate" type="text/html" title="How do I format my GridView bound column differently?" /><published>2007-11-06T00:04:33-07:00</published><updated>2007-11-06T00:04:33-07:00</updated><id>https://www.josephguadagno.net/2007/11/06/how-do-i-format-my-gridview-bound-column-differently</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/06/how-do-i-format-my-gridview-bound-column-differently/"><![CDATA[<p>Replace the <em>ColumnName</em> value with the name of your column. Replace DataFormatString property with the format string of your choosing.  A good reference for .NET string formats is available at <a href="https://john-sheehan.com/blog/index.php/net-cheat-sheets/">https://john-sheehan.com/blog/index.php/net-cheat-sheets/</a></p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;asp:GridView</span> <span class="na">ID=</span><span class="s">"GridView1"</span> <span class="na">runat=</span><span class="s">“server”</span><span class="nt">&gt;</span>
  <span class="nt">&lt;columns&gt;</span>
    <span class="nt">&lt;asp:BoundField</span> <span class="na">DataField=</span><span class="s">"ColumnName"</span>
      <span class="na">DataFormatString=</span><span class="s">"{0:M-dd-yyyy}"</span>
      <span class="na">HtmlEncode=</span><span class="s">"false"</span>
      <span class="na">HeaderText=</span><span class="s">"ColumnName"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;/columns&gt;</span>
<span class="nt">&lt;/asp:GridView&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="ASP.NET" /><category term="GridView" /><category term=".NET" /><summary type="html"><![CDATA[Replace the ColumnName value with the name of your column. Replace DataFormatString property with the format string of your choosing.  A good reference for .NET string formats is available at https://john-sheehan.com/blog/index.php/net-cheat-sheets/]]></summary></entry><entry><title type="html">How to use the Google AJAX Search API on your website.</title><link href="https://www.josephguadagno.net/2007/11/06/how-to-use-the-google-ajax-search-api-on-your-web-site" rel="alternate" type="text/html" title="How to use the Google AJAX Search API on your website." /><published>2007-11-06T00:04:33-07:00</published><updated>2007-11-06T00:04:33-07:00</updated><id>https://www.josephguadagno.net/2007/11/06/how-to-use-the-google-ajax-search-api-on-your-web-site</id><content type="html" xml:base="https://www.josephguadagno.net/2007/11/06/how-to-use-the-google-ajax-search-api-on-your-web-site"><![CDATA[<h2 id="the-google-ajax-search-api">The Google AJAX Search API</h2>

<p>The <a href="https://code.google.com/apis/ajaxsearch/" target="_blank">Google AJAX Search API</a> is a Javascript library that allows you to embed Google Search in your web pages and other web applications. To use the API, you will first need to sign up for an <a href="https://code.google.com/apis/ajaxsearch/signup.html" target="_blank">API key</a>. The Google AJAX Search API provides simple web objects that perform inline searches over a number of Google services (Web Search, Local Search, Video Search, Blog Search, News Search, Book Search, and Image Search). If your web page is designed to help users create content (e.g. message boards, blogs, etc.), the API is designed to support these activities by allowing them to copy search results directly into their messages.</p>

<h2 id="if-so-then-why-wrap-it">If so, then why wrap it?</h2>

<p>I wrapped the Google API up into a separate component so that I can use the Google API in more than one web page without having to recreate all of the objects over and over again. Most of the setup of the Google API is repetitive across multiple instances of it. The way the Google API was wrapped should all others to use the object with only a small modification. See the Samples section below.</p>

<h2 id="the-mygsearch-object">The MyGSearch object</h2>

<p>Download the <a href="/assets/downloads/MyGSearch.zip">MyGSearch JavaScript and HTML files</a>.</p>

<h3 id="properties">Properties</h3>

<table>
  <thead>
    <tr>
      <th>Property Name</th>
      <th><strong>Property Type</strong></th>
      <th><strong>Purpose</strong></th>
      <th><strong>Default Value</strong></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>HideMainContent</td>
      <td>boolean</td>
      <td>Indicated whether or not the Main Content should be shown while the search results are visible</td>
      <td>true</td>
    </tr>
    <tr>
      <td>SearchControl</td>
      <td>google.SearchControl</td>
      <td>This is a reference to the google.SearchControl</td>
      <td>null</td>
    </tr>
    <tr>
      <td>WatermarkText</td>
      <td>string</td>
      <td>This is the text to be displayed as the watermark. The watermark text will be visible if the search input in empty</td>
      <td>Search for …</td>
    </tr>
    <tr>
      <td>IsInitialized</td>
      <td>boolean</td>
      <td>Indicates if the MyGSearch is initialized, this value is set after the Initialize function is completed.</td>
      <td>false</td>
    </tr>
    <tr>
      <td>Divs</td>
      <td>object</td>
      <td>Contains all of the divs that are used for the MyGSearch object</td>
      <td>new Object()</td>
    </tr>
    <tr>
      <td>CSS</td>
      <td>object</td>
      <td>Contains all of the CSS classes used for the MyGSearch object</td>
      <td>new Object()</td>
    </tr>
  </tbody>
</table>

<h4 id="divs-object">Divs object</h4>

<table>
  <thead>
    <tr>
      <th>Property Name</th>
      <th>Property Type</th>
      <th>Purpose</th>
      <th>Default Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>SearchResults</td>
      <td>DOM object (DIV)</td>
      <td>This is the DIV tag that the Google API will replace the contents of the search results.</td>
      <td>null</td>
    </tr>
    <tr>
      <td>SearchInputBox</td>
      <td>DOM object (INPUT)</td>
      <td>This is the INPUT control that the Google API will use to execute searches.</td>
      <td>null</td>
    </tr>
    <tr>
      <td>SearchBranding</td>
      <td>DOM object (DIV)</td>
      <td>This is the DIV that the Google API will place the required “powered by Google” branding.</td>
      <td>null</td>
    </tr>
    <tr>
      <td>SearchStarting</td>
      <td>DOM object (DIV)</td>
      <td>This is the DIV tag that will be shown while the searching is happening.</td>
      <td>null</td>
    </tr>
    <tr>
      <td>SearchClose</td>
      <td>DOM object (DIV)</td>
      <td>This is the DIV tag that will display the “Close Search Results” content. This is used to reset the page back to its normal state.</td>
      <td>null</td>
    </tr>
    <tr>
      <td>MainContent</td>
      <td>DOM object (DIV)</td>
      <td>This is the DIV of your main content. This will be hidden while searching if the HideMainContent property is true until the ClearResults is called.</td>
      <td>null</td>
    </tr>
  </tbody>
</table>

<h4 id="css-object">CSS Object</h4>

<table>
  <thead>
    <tr>
      <th>Property Name</th>
      <th>Property Type</th>
      <th>Purpose</th>
      <th>Default Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Watermark</td>
      <td>string</td>
      <td>The CSS style to use when the search box input control needs to display the watermark</td>
      <td> </td>
    </tr>
    <tr>
      <td>InputBox</td>
      <td>string</td>
      <td>The CSS style when the search box input control is being edited or not displaying the watermark</td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h3 id="methods">Methods</h3>

<h4 id="initialize">Initialize</h4>

<p>Initializes the all of the properties and google.search object.</p>

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Property Type</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>resultsDiv</td>
      <td>DOM object (DOM object)</td>
      <td>This is where all of the results will be replaced by the Google API. This parameter will update the Divs.SearchResults property.</td>
    </tr>
    <tr>
      <td>inputBox</td>
      <td>DOM object (INPUT type=”text”)</td>
      <td>This is the text box that the Google API will hook to execute the searches. This parameter will update the Divs.SearchInputBox property.</td>
    </tr>
    <tr>
      <td>brandingDiv</td>
      <td>DOM object (DIV)</td>
      <td>This is the div that the Google API will place the required “Powered by Google” branding. This parameter will update the Divs.SearchBranding property.</td>
    </tr>
    <tr>
      <td>mainDiv</td>
      <td>DOM object (DIV)</td>
      <td>This is the DIV that contains you main content. This DIV will be hidden while searching if the HideMainContent property is set to <em>true</em>. This parameter will update the Divs.MainContent property.</td>
    </tr>
    <tr>
      <td>startSearchingDiv</td>
      <td>DOM object (DIV)</td>
      <td>This DIV will be displayed while the Google API is executing the search. It will be hidden when the search is complete. This parameter will update the Divs.StartSearching property.</td>
    </tr>
    <tr>
      <td>closeDiv</td>
      <td>DOM object (DIV)</td>
      <td>This DIV will be displayed after the Google API has completed the search. This DIV allows you to provide a mechanism to close the search results, which can be done by calling the <em>ClearResults</em> methods. This parameter will update the Divs.SearchClose property.</td>
    </tr>
  </tbody>
</table>

<h4 id="clearresults">ClearResults</h4>

<p>This method performs the following:</p>

<ul>
  <li>Clears the search results</li>
  <li>Hides the Divs.SearchClose DIV</li>
  <li>Calls the onInputBlur method</li>
  <li>Hides the Divs.SearchResults DIV</li>
  <li>Shows the Divs.MainContent DIV</li>
</ul>

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Property Type</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>none</td>
      <td> </td>
      <td> </td>
    </tr>
  </tbody>
</table>

<h4 id="execute">Execute</h4>

<p>Executes a Google search based on the value specified in the query parameter.</p>

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Property Type</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>query</td>
      <td>string</td>
      <td>The value to search google for.</td>
    </tr>
  </tbody>
</table>

<h4 id="onsearchcomplete">onSearchComplete</h4>

<p>This method is called by the Google API when the search is complete. This method does the following.</p>

<ul>
  <li>Hides the SearchStarting DIV</li>
  <li>Shows the SearchResults DIV</li>
  <li>Shows the SearchClose DIV</li>
  <li>Hides or Shows the MainContent DIV based on the value of the <em>HideMainContent</em> property.</li>
</ul>

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Property Type</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>searchControl</td>
      <td>google.searchControl</td>
      <td>The Google search control that is calling this method.</td>
    </tr>
    <tr>
      <td>searcher</td>
      <td>google.searcher</td>
      <td>The Google searcher that is complete.</td>
    </tr>
  </tbody>
</table>

<h4 id="onstartsearching">onStartSearching</h4>

<p>This event is raised by the google.search component. In it we will Hide the Main Content and show the SearchStarting Div</p>

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Property Type</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>searchControl</td>
      <td>google.searchControl</td>
      <td>The Google search control that is calling this method.</td>
    </tr>
    <tr>
      <td>searcher</td>
      <td>google.searcher</td>
      <td>The Google search that has started searching.</td>
    </tr>
    <tr>
      <td>query</td>
      <td>string</td>
      <td>The text that is being searched for.</td>
    </tr>
  </tbody>
</table>

<h4 id="oninputblur">onInputBlur</h4>

<p>This mimics the Ajax ASP.NET watermark extender control by changing the CSS class of the input box based on the value of it. This is called when the focus leaves the input box.</p>

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Property Type</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>gSearch</td>
      <td>A MyGSearch object</td>
      <td>A reference to the myGSearch object that you want to blur.</td>
    </tr>
  </tbody>
</table>

<h4 id="oninputfocus">onInputFocus</h4>

<p>This mimics the Ajax ASP.NET watermark extender control by changing the CSS class of the input box based on the value of it. This is called when the input box has focus.</p>

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Property Type</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>gSearch</td>
      <td>A MyGSearch object</td>
      <td>A reference to the myGSearch object that you want to blur.</td>
    </tr>
  </tbody>
</table>

<h2 id="using-the-mygsearch-object">Using the MyGSearch object</h2>

<p>There are two easy steps to using the MyGSearch objects. This, of course, assumes that you have obtained a Google API key from <a href="https://code.google.com/apis/ajaxsearch/signup.html" target="_blank">https://code.google.com/apis/ajaxsearch/signup.html</a>.</p>

<h3 id="step-1-create-the-html-content">Step 1: Create the HTML content</h3>

<p>In order for you to use the MyGSearch control, you must create 5 DIVs and one input box. The five Divs map to the five Divs object properties, SearchResults, SearchBranding, MainContent, SearchStarting, and SearchClose. The input box will be used for the Google API to execute the search.</p>

<p>The HTML content will exist in two parts, the first the HEAD section.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="rouge-code"><pre>
<span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"</span>
  <span class="na">data-wp-preserve=</span><span class="s">"%3Cscript%20src%3D%22http%3A%2F%2Fwww.google.com%2Fjsapi%3Fkey%3DYOUR_KEY_HERE%22%20type%3D%22text%2Fjavascript%22%3E%3C%2Fscript%3E"</span>
  <span class="na">data-mce-resize=</span><span class="s">"false"</span>
  <span class="na">data-mce-placeholder=</span><span class="s">"1"</span>
  <span class="na">class=</span><span class="s">"mce-object"</span>
  <span class="na">width=</span><span class="s">"20"</span> <span class="na">height=</span><span class="s">"20"</span> <span class="na">alt=</span><span class="s">"&amp;lt;script&amp;gt;"</span>
  <span class="na">title=</span><span class="s">"&amp;lt;script&amp;gt;"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"</span>
  <span class="na">data-wp-preserve=</span><span class="s">"%3Cscript%20src%3D%22MyGSearch.js%22%20type%3D%22text%2Fjavascript%22%3E%3C%2Fscript%3E"</span>
  <span class="na">data-mce-resize=</span><span class="s">"false"</span>
  <span class="na">data-mce-placeholder=</span><span class="s">"1"</span>
  <span class="na">class=</span><span class="s">"mce-object"</span>
  <span class="na">width=</span><span class="s">"20"</span> <span class="na">height=</span><span class="s">"20"</span> <span class="na">alt=</span><span class="s">"&amp;lt;script&amp;gt;"</span>
  <span class="na">title=</span><span class="s">"&amp;lt;script&amp;gt;"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"</span>
  <span class="na">data-wp-preserve=</span><span class="s">"%3Cscript%20src%3D%22Util.js%22%20type%3D%22text%2Fjavascript%22%3E%3C%2Fscript%3E"</span>
  <span class="na">data-mce-resize=</span><span class="s">"false"</span>
  <span class="na">data-mce-placeholder=</span><span class="s">"1"</span>
  <span class="na">class=</span><span class="s">"mce-object"</span> <span class="na">width=</span><span class="s">"20"</span> <span class="na">height=</span><span class="s">"20"</span> <span class="na">alt=</span><span class="s">"&amp;lt;script&amp;gt;"</span>
  <span class="na">title=</span><span class="s">"&amp;lt;script&amp;gt;"</span> <span class="nt">/&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Remember to replace YOUR_KEY_HERE with your Google API key. Then in the body of the HTML page you add in the required DIVs and input box.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"divSearchClose"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"javascript:ClearResults();"</span> <span class="na">id=</span><span class="s">"searchCloseLink"</span><span class="nt">&gt;</span>Close Search Results<span class="nt">&lt;/a&gt;</span>
<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;div&gt;</span>
  <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">id=</span><span class="s">"txtSearchInput"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"submit"</span> <span class="na">value=</span><span class="s">"Search"</span> <span class="na">id=</span><span class="s">"Search"</span> <span class="na">onClick=</span><span class="s">"Search();"</span><span class="nt">/&gt;</span>
  <span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"submit"</span> <span class="na">value=</span><span class="s">"Clear"</span> <span class="na">id=</span><span class="s">"btnClear"</span> <span class="na">onClick=</span><span class="s">"ClearResults();"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"divGoogleBranding"</span><span class="nt">&gt;&lt;/div&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"divMainContent"</span><span class="nt">&gt;</span>
  This is where your main text goes.  
<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"divSearchStarting"</span><span class="nt">&gt;</span>
  Searching for data...
<span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"divSearchResults"</span><span class="nt">&gt;&lt;/div&gt;</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>When you are done you should have something like this, which is the google.html example file.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
</pre></td><td class="rouge-code"><pre><span class="nt">&lt;HTML&gt;</span>
<span class="nt">&lt;head&gt;</span>
  <span class="nt">&lt;title&gt;</span>Google AJAX Search API test<span class="nt">&lt;/title&gt;</span>
  <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"</span>
    <span class="na">data-wp-preserve=</span><span class="s">"%3Cscript%20src%3D%22http%3A%2F%2Fwww.google.com%2Fjsapi%3Fkey%3DYOUR_API_KEY%22%20type%3D%22text%2Fjavascript%22%3E%3C%2Fscript%3E"</span>
    <span class="na">data-mce-resize=</span><span class="s">"false"</span>
    <span class="na">data-mce-placeholder=</span><span class="s">"1"</span>
    <span class="na">class=</span><span class="s">"mce-object"</span> <span class="na">width=</span><span class="s">"20"</span> <span class="na">height=</span><span class="s">"20"</span> <span class="na">alt=</span><span class="s">"&amp;lt;script&amp;gt;"</span>
    <span class="na">title=</span><span class="s">"&amp;lt;script&amp;gt;"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"Util.js"</span> <span class="na">type=</span><span class="s">"text/javascript"</span> <span class="nt">/&gt;</span>
  <span class="o">&lt;</span><span class="nx">script</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">MyGSearch.js</span><span class="dl">"</span> <span class="nx">type</span><span class="o">=</span><span class="dl">"</span><span class="s2">text/javascript</span><span class="dl">"</span> <span class="o">/&gt;</span>
  <span class="o">&lt;</span><span class="nx">script</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">GoogleSearch.js</span><span class="dl">"</span> <span class="nx">type</span><span class="o">=</span><span class="dl">"</span><span class="s2">text/javascript</span><span class="dl">"</span> <span class="o">/&gt;</span>

<span class="o">&lt;</span><span class="nx">img</span> <span class="nx">src</span><span class="o">=</span><span class="dl">"</span><span class="s2">data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7</span><span class="dl">"</span>
  <span class="nx">data</span><span class="o">-</span><span class="nx">wp</span><span class="o">-</span><span class="nx">preserve</span><span class="o">=</span><span class="dl">"</span><span class="s2">%3Cstyle%20type%3D%22text%2Fcss%22%3E%0A%09%09.watermark%0A%09%09%7B%0A%09%09%09height%3A22px%3B%0A%09%09%09width%3A150px%3B%0A%09%09%09padding%3A2px%200%200%202px%3B%0A%09%09%09border%3A1px%20solid%20%23BEBEBE%3B%0A%09%09%09background-color%3A%23F0F8FF%3B%0A%09%09%09color%3Agray%3B%0A%09%09%09font-style%3A%20italic%3B%0A%09%09%7D%0A%09%09.search%0A%09%09%7B%0A%09%09%09height%3A22px%3B%0A%09%09%09width%3A150px%3B%0A%09%09%09padding%3A2px%200%200%202px%3B%0A%09%09%09border%3A1px%20solid%20%23BEBEBE%3B%0A%09%09%09background-color%3Awhite%3B%0A%09%09%09color%3Ablack%3B%0A%09%09%7D%0A%09%3C%2Fstyle%3E</span><span class="dl">"</span>
  <span class="nx">data</span><span class="o">-</span><span class="nx">mce</span><span class="o">-</span><span class="nx">resize</span><span class="o">=</span><span class="dl">"</span><span class="s2">false</span><span class="dl">"</span>
  <span class="nx">data</span><span class="o">-</span><span class="nx">mce</span><span class="o">-</span><span class="nx">placeholder</span><span class="o">=</span><span class="dl">"</span><span class="s2">1</span><span class="dl">"</span>
  <span class="kd">class</span><span class="o">=</span><span class="dl">"</span><span class="s2">mce-object</span><span class="dl">"</span> <span class="nx">width</span><span class="o">=</span><span class="dl">"</span><span class="s2">20</span><span class="dl">"</span> <span class="nx">height</span><span class="o">=</span><span class="dl">"</span><span class="s2">20</span><span class="dl">"</span> <span class="nx">alt</span><span class="o">=</span><span class="dl">"</span><span class="s2">&amp;lt;style&amp;gt;</span><span class="dl">"</span>
  <span class="nx">title</span><span class="o">=</span><span class="dl">"</span><span class="s2">&amp;lt;style&amp;gt;</span><span class="dl">"</span> <span class="o">/&gt;</span>
<span class="o">&lt;</span><span class="sr">/head</span><span class="err">&gt;
</span><span class="o">&lt;</span><span class="nx">body</span><span class="o">&gt;</span>
  <span class="nx">Google</span> <span class="nx">Ajax</span> <span class="nx">Search</span> <span class="nx">API</span> <span class="nx">Test</span>
  <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">divSearchClose</span><span class="dl">"</span><span class="o">&gt;</span>
    <span class="o">&lt;</span><span class="nx">a</span> <span class="nx">href</span><span class="o">=</span><span class="dl">"</span><span class="s2">javascript:ClearResults();</span><span class="dl">"</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">searchCloseLink</span><span class="dl">"</span><span class="o">&gt;</span>
      <span class="nx">Close</span> <span class="nx">Search</span> <span class="nx">Results</span>
    <span class="o">&lt;</span><span class="sr">/a</span><span class="err">&gt;
</span>  <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>  <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
    <span class="o">&lt;</span><span class="nx">input</span> <span class="nx">type</span><span class="o">=</span><span class="dl">"</span><span class="s2">text</span><span class="dl">"</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">txtSearchInput</span><span class="dl">"</span> <span class="o">/&gt;</span>
    <span class="o">&lt;</span><span class="nx">input</span> <span class="nx">type</span><span class="o">=</span><span class="dl">"</span><span class="s2">submit</span><span class="dl">"</span> <span class="nx">value</span><span class="o">=</span><span class="dl">"</span><span class="s2">Search</span><span class="dl">"</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">Search</span><span class="dl">"</span> <span class="nx">onClick</span><span class="o">=</span><span class="dl">"</span><span class="s2">Search();</span><span class="dl">"</span><span class="o">/&gt;</span>
    <span class="o">&lt;</span><span class="nx">input</span> <span class="nx">type</span><span class="o">=</span><span class="dl">"</span><span class="s2">submit</span><span class="dl">"</span> <span class="nx">value</span><span class="o">=</span><span class="dl">"</span><span class="s2">Clear</span><span class="dl">"</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">btnClear</span><span class="dl">"</span> <span class="nx">onClick</span><span class="o">=</span><span class="dl">"</span><span class="s2">ClearResults();</span><span class="dl">"</span><span class="o">/&gt;</span>
  <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>  <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">divGoogleBranding</span><span class="dl">"</span><span class="o">&gt;&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>  <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">divMainContent</span><span class="dl">"</span><span class="o">&gt;</span>
    <span class="nx">This</span> <span class="nx">is</span> <span class="nx">where</span> <span class="nx">your</span> <span class="nx">main</span> <span class="nx">text</span> <span class="nx">goes</span><span class="p">.</span>
  <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>  <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">divSearchStarting</span><span class="dl">"</span><span class="o">&gt;</span><span class="nx">Searching</span> <span class="k">for</span> <span class="nx">data</span><span class="p">...</span><span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span>  <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">id</span><span class="o">=</span><span class="dl">"</span><span class="s2">divSearchResults</span><span class="dl">"</span><span class="o">&gt;&lt;</span><span class="sr">/div</span><span class="err">&gt;
</span><span class="o">&lt;</span><span class="sr">/body</span><span class="err">&gt;
</span><span class="o">&lt;</span><span class="sr">/HTML</span><span class="err">&gt;
</span></pre></td></tr></tbody></table></code></pre></div></div>

<p>On line 3, we create a global JavaScript variable to hold a reference to the MyGSearch object. The InitializeMyGSearch function starts on line 9. Line 11 checks to see if the object already exists and is initialized if so exits the function. Line 14 - 21 initialize the MyGSearch object and sets the basic properties of it. Lines 24 - 26, hide the search related divs. Line 28 we get a reference to the input box so that we can add our OnBlur and OnFocus events to the control. Line 34, we call the onInputBlur methods of the MyGSearch to set the watermark. Line 38 loads all of the Google Search APIs. Line 41 calls a Google helper function to call InitializeMyGSearch once the form loads. That’s it.</p>

<h2 id="samples">Samples</h2>

<p>Within the ZIP file I included two HTML pages that demonstrate how to use the MyGSearch object. The first sample <code class="language-plaintext highlighter-rouge">google.html</code> provides the required code for a simple search. The required Google branding div is present as well as the search results, search close, search start and main divs.</p>

<p>The second example <code class="language-plaintext highlighter-rouge">page404.html</code> is a page that shows what would be required to use the Google AJAX Search API to query google when a user tries to access a page that does not exist on your server. In addition, this example demonstrates how you can automatically search for some text when the page loads.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre><span class="c1">// Add a search for the site search</span>
<span class="nx">searcher</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">google</span><span class="p">.</span><span class="nx">search</span><span class="p">.</span><span class="nx">WebSearch</span><span class="p">();</span>
<span class="c1">// This is the label for the tab</span>
<span class="nx">searcher</span><span class="p">.</span><span class="nx">setUserDefinedLabel</span><span class="p">(</span><span class="dl">"</span><span class="s2">JosephGuadagno.net</span><span class="dl">"</span><span class="p">);</span>
<span class="c1">// This restricts the search to just josephguadagno.net</span>
<span class="nx">searcher</span><span class="p">.</span><span class="nx">setSiteRestriction</span><span class="p">(</span><span class="dl">"</span><span class="s2">josephguadagno.net</span><span class="dl">"</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Here you would change lines 4 and 6 to match whatever domain you want. Line 4 is the label for the tab within the results and line 6 is the domain you want to search.</p>

<p>Other possible modifications include adding additional Google searchers to search other domains or other file types like Images, Video or Blogs. You could also change the Google draw objects to draw the search results in a result set.</p>

<h2 id="references">References</h2>

<ul>
  <li>Get your Google API Key: <a href="https://code.google.com/apis/ajaxsearch/signup.html" target="_blank">https://code.google.com/apis/ajaxsearch/signup.html</a></li>
  <li>Google AJAX API: <a href="https://code.google.com/apis/ajaxsearch/" target="_blank">https://code.google.com/apis/ajaxsearch/</a></li>
  <li>Google AJAX API Documentation: <a href="https://code.google.com/apis/ajaxsearch/documentation/" target="_blank">https://code.google.com/apis/ajaxsearch/documentation/</a></li>
  <li>Google AJAX API User Group: <a href="https://groups-beta.google.com/group/Google-AJAX-Search-API" target="_blank">https://groups-beta.google.com/group/Google-AJAX-Search-API</a></li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Articles" /><category term="Google" /><category term="Web" /><summary type="html"><![CDATA[The Google AJAX Search API]]></summary></entry><entry><title type="html">Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries</title><link href="https://www.josephguadagno.net/2007/08/02/framework-design-guidelines-conventions-idioms-and-patterns-for-reusable-net-libraries/" rel="alternate" type="text/html" title="Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries" /><published>2007-08-01T17:14:17-07:00</published><updated>2007-08-01T17:14:17-07:00</updated><id>https://www.josephguadagno.net/2007/08/02/framework-design-guidelines-conventions-idioms-and-patterns-for-reusable-net-libraries</id><content type="html" xml:base="https://www.josephguadagno.net/2007/08/02/framework-design-guidelines-conventions-idioms-and-patterns-for-reusable-net-libraries/"><![CDATA[<p>You should go out and buy this book this book immediately.  This is the perfect desktop reference for designing components and base class libraries. What better way to learn how to design / implement a library then from the Program Manager would designed the .NET framework.</p>

<p>By the end of the book you will learn:</p>

<ul>
  <li>The general philosophy of framework design</li>
  <li>Principles and guidelines that are fundamental to overall framework design</li>
  <li>Naming guidelines for the various parts of a framework, such as namespaces, types, and members</li>
  <li>Guidelines for the design of types and members of types</li>
  <li>Issues and guidelines that are important to ensure appropriate extensibility in your framework</li>
  <li>Guidelines for working with exceptions, the preferred error reporting mechanism in the .NET Framework and WinFX</li>
  <li>Guidelines for extending and using types that commonly appear in frameworks</li>
  <li>Guidelines for and examples of common framework design patterns</li>
</ul>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[You should go out and buy this book this book immediately. This is the perfect desktop reference for designing components and base class libraries. What better way to learn how to design / implement a library then from the Program Manager would designed the .NET framework.]]></summary></entry><entry><title type="html">Essential C# 2.0</title><link href="https://www.josephguadagno.net/2007/08/02/essential-c-2-0/" rel="alternate" type="text/html" title="Essential C# 2.0" /><published>2007-08-01T17:13:39-07:00</published><updated>2007-08-01T17:13:39-07:00</updated><id>https://www.josephguadagno.net/2007/08/02/essential-c-2-0</id><content type="html" xml:base="https://www.josephguadagno.net/2007/08/02/essential-c-2-0/"><![CDATA[<p>This book is a good starting point for learning the new features of C# 2.0.  The only downside of the book is that there were several errors found in the book samples.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book is a good starting point for learning the new features of C# 2.0. The only downside of the book is that there were several errors found in the book samples.]]></summary></entry><entry><title type="html">Developing Microsoft ASP.NET Server Controls and Components</title><link href="https://www.josephguadagno.net/2007/08/02/developing-microsoft-asp-net-server-controls-and-components/" rel="alternate" type="text/html" title="Developing Microsoft ASP.NET Server Controls and Components" /><published>2007-08-01T17:12:56-07:00</published><updated>2007-08-01T17:12:56-07:00</updated><id>https://www.josephguadagno.net/2007/08/02/developing-microsoft-asp-net-server-controls-and-components</id><content type="html" xml:base="https://www.josephguadagno.net/2007/08/02/developing-microsoft-asp-net-server-controls-and-components/"><![CDATA[<p>This book is a comprehensive guide to building ASP.NET server controls and by comprehensive I me comprehensive, at 1224 pages, this book goes into all aspects of designing and developer controls.  There are 5 chapters on security, membership providers and role management. 4 chapters on WebParts, and more.  If you want to built server controls or components this is a <strong>must</strong> have.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book is a comprehensive guide to building ASP.NET server controls and by comprehensive I me comprehensive, at 1224 pages, this book goes into all aspects of designing and developer controls. There are 5 chapters on security, membership providers and role management. 4 chapters on WebParts, and more. If you want to built server controls or components this is a must have.]]></summary></entry><entry><title type="html">Essential Windows Workflow Foundation</title><link href="https://www.josephguadagno.net/2007/08/02/essential-windows-workflow-foundation/" rel="alternate" type="text/html" title="Essential Windows Workflow Foundation" /><published>2007-08-01T17:12:20-07:00</published><updated>2007-08-01T17:12:20-07:00</updated><id>https://www.josephguadagno.net/2007/08/02/essential-windows-workflow-foundation</id><content type="html" xml:base="https://www.josephguadagno.net/2007/08/02/essential-windows-workflow-foundation/"><![CDATA[<p>This book was quite a hard read.  It covers all of the types of workflows that are supported by the libraries. Unfortunately, it is a bit dry and the samples do not go far from the typical “Hello World” samples.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book was quite a hard read. It covers all of the types of workflows that are supported by the libraries. Unfortunately, it is a bit dry and the samples do not go far from the typical “Hello World” samples.]]></summary></entry><entry><title type="html">Essential ASP.NET 2.0</title><link href="https://www.josephguadagno.net/2007/08/02/essential-asp-net-2-0/" rel="alternate" type="text/html" title="Essential ASP.NET 2.0" /><published>2007-08-01T17:11:45-07:00</published><updated>2007-08-01T17:11:45-07:00</updated><id>https://www.josephguadagno.net/2007/08/02/essential-asp-net-2-0</id><content type="html" xml:base="https://www.josephguadagno.net/2007/08/02/essential-asp-net-2-0/"><![CDATA[<p>This book covers new features of ASP.NET 2.0. This book assumes that you are familiar with ASP.NET (Essential ASP.NET) as such it covers web parts, membership providers and other new features. You should get this book if you need to get up to speed on what’s new in ASP.NET 2.0.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book covers new features of ASP.NET 2.0. This book assumes that you are familiar with ASP.NET (Essential ASP.NET) as such it covers web parts, membership providers and other new features. You should get this book if you need to get up to speed on what’s new in ASP.NET 2.0.]]></summary></entry><entry><title type="html">.NET Framework Standard Library Annotated Reference, Volume 2: Networking Library, Reflection Library, and XML Library</title><link href="https://www.josephguadagno.net/2007/08/02/net-framework-standard-library-annotated-reference-volume-2-networking-library-reflection-library-and-xml-library/" rel="alternate" type="text/html" title=".NET Framework Standard Library Annotated Reference, Volume 2: Networking Library, Reflection Library, and XML Library" /><published>2007-08-01T17:11:06-07:00</published><updated>2007-08-01T17:11:06-07:00</updated><id>https://www.josephguadagno.net/2007/08/02/net-framework-standard-library-annotated-reference-volume-2-networking-library-reflection-library-and-xml-library</id><content type="html" xml:base="https://www.josephguadagno.net/2007/08/02/net-framework-standard-library-annotated-reference-volume-2-networking-library-reflection-library-and-xml-library/"><![CDATA[<p>This book and its companion book, <a href="/2007/08/02/framework-design-guidelines-conventions-idioms-and-patterns-for-reusable-net-libraries/" target="_blank">.NET Framework Standard Library Annotated Reference, Volume 1</a>, are essential reference material that every .NET developer must have on their desk. This book goes into the .NET framework classes in great detail with ample samples.</p>]]></content><author><name>Joseph Guadagno</name></author><category term="Books" /><category term="Book Reviews" /><summary type="html"><![CDATA[This book and its companion book, .NET Framework Standard Library Annotated Reference, Volume 1, are essential reference material that every .NET developer must have on their desk. This book goes into the .NET framework classes in great detail with ample samples.]]></summary></entry></feed>