tag:blogger.com,1999:blog-17738218770201770262024-03-09T00:39:31.913-08:00Medhat ElmasryMedhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.comBlogger228125tag:blogger.com,1999:blog-1773821877020177026.post-42480533568409514162024-02-29T10:16:00.000-08:002024-03-05T17:40:14.792-08:00OpenAI Function Calling with Semantic Kernel, C#, & Entity Framework<p>In this article, we will create a Semantic Kernel plugin that contains four functions that interact with live SQLite data. Entity Framework will be used to access the SQLite database. The end result is to use the powers of the OpenAI natural language models to ask questions and get answers about our custom data.</p><p>Source code: <a href="https://github.com/medhatelmasry/EfFuncCallSK">https://github.com/medhatelmasry/EfFuncCallSK</a></p><p>Companion Video: <a href="https://youtu.be/4sKRwflEyHk">https://youtu.be/4sKRwflEyHk</a></p><h2 style="text-align: left;">Getting Started</h2><p>Let’s start by creating an ASP.NET Razor pages web application. Select a suitable working folder on your computer, then enter the following terminal window commands:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">dotnet new razor --auth individual -o EfFuncCallSK</span></div><div style="text-align: left;"><span style="font-family: courier;">cd EfFuncCallSK</span></div></blockquote><p>Te above creates a Razor Pages app with support for Entity Framework and SQLite.</p><p>Add these packages:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">dotnet add package CsvHelper</span></div><div style="text-align: left;"><span style="font-family: courier;">dotnet add package Microsoft.SemanticKernel </span></div><div style="text-align: left;"><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore.Design</span></div><div style="text-align: left;"><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore.Tools</span></div><div style="text-align: left;"><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore</span></div><div style="text-align: left;"><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore.SQLite.Design</span></div></blockquote><p>The <i>CsvHelper</i> package will help us load a list of products from a CSV file named <i>students.csv</i> and hydrate a list of <i>Student</i> objects. The second package is needed to work with Semantic Kernel. The rest of the packages support Entity Framework and SQLite.</p><h2 style="text-align: left;">Let’s Code</h2><h3 style="text-align: left;">appsettings.json</h3><p>Add these to <i>appsettings.json</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">"AIService": "OpenAI", /* Azure or OpenAI */</span></div><div style="text-align: left;"><span style="font-family: courier;">"AzureOpenAiSettings": {</span></div><div style="text-align: left;"><span style="font-family: courier;"> "Endpoint": "https://YOUR_RESOURCE_NAME.openai.azure.com/",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "Model": "gpt-35-turbo",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "ApiKey": "fake-key-fake-key-fake-key-fake-key"</span></div><div style="text-align: left;"><span style="font-family: courier;">},</span></div><div style="text-align: left;"><span style="font-family: courier;">"OpenAiSettings": {</span></div><div style="text-align: left;"><span style="font-family: courier;"> "ModelType": "gpt-3.5-turbo",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "ApiKey": "fake-key-fake-key-fake-key-fake-key"</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p>The first setting allows you to choose between using OpenAI or Azure OpenAI.</p><p>Of course, you need to adjust the endpoint setting with the appropriate value that pertains to the OpenAI and Azure OpenAI services. Also, enter the correct value for the <i>ApiKey</i>.</p><p>NOTE: You can use OpenAI or Azure OpenAI, or both.</p><h3 style="text-align: left;">Data</h3><p>Create a folder named <i>Models</i>. Inside the <i>Models</i> folder, add the following <i>Student</i> class: </p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public class Student {</span></div><div style="text-align: left;"><span style="font-family: courier;"> public int StudentId { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"> public string? FirstName { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"> public string? LastName { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"> public string? School { get; set; }</span></div></blockquote><div style="text-align: left;"> </div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public override string ToString() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> return $"Student ID: {StudentId}, First Name: {FirstName}, Last Name: {LastName}, School: {School}";</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p style="text-align: left;">Developers like having sample data when building data driven applications. Therefore, we will create sample data to ensure that our application behaves as expected. Copy the following data and save it in a text file <i>wwwroot/<a href="https://gist.github.com/medhatelmasry/bd83812406665cd7584bb994f6e7704e">students.csv</a></i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">StudentId,FirstName,LastName,School</span></div><div style="text-align: left;"><span style="font-family: courier;">1,Tom,Max,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">2,Ann,Fay,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">3,Joe,Sun,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">4,Sue,Fox,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">5,Ben,Ray,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">6,Zoe,Cox,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">7,Sam,Ray,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">8,Dan,Ash,Medicine</span></div><div style="text-align: left;"><span style="font-family: courier;">9,Pat,Lee,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">10,Kim,Day,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">11,Tim,Rex,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">12,Rob,Ram,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">13,Jan,Fry,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">14,Jim,Tex,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">15,Ben,Kid,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">16,Mia,Chu,Medicine</span></div><div style="text-align: left;"><span style="font-family: courier;">17,Ted,Tao,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">18,Amy,Day,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">19,Ian,Roy,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">20,Liz,Kit,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">21,Mat,Tan,Medicine</span></div><div style="text-align: left;"><span style="font-family: courier;">22,Deb,Roy,Medicine</span></div><div style="text-align: left;"><span style="font-family: courier;">23,Ana,Ray,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">24,Lyn,Poe,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">25,Amy,Raj,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">26,Kim,Ash,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">27,Bec,Kid,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">28,Eva,Fry,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">29,Eli,Lap,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">30,Sam,Yim,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">31,Joe,Hui,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">32,Liz,Jin,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">33,Ric,Kuo,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">34,Pam,Mak,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">35,Cat,Yao,Medicine</span></div><div style="text-align: left;"><span style="font-family: courier;">36,Lou,Zhu,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">37,Tom,Dag,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">38,Stu,Day,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">39,Tom,Gad,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">40,Bob,Bee,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">41,Jim,Ots,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">42,Tom,Mag,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">43,Hal,Doe,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">44,Roy,Kim,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">45,Vis,Cox,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">46,Kay,Aga,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">47,Reo,Hui,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">48,Bob,Roe,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">49,Jay,Eff,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">50,Eva,Chu,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">51,Lex,Rae,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">52,Lin,Dex,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">53,Tom,Dag,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">54,Ben,Shy,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">55,Rob,Bos,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">56,Ali,Mac,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">57,Edi,Gee,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">58,Eva,Cao,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">59,Jun,Lam,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">60,Eli,Tao,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">61,Ana,Bay,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">62,Gil,Tal,Mining</span></div><div style="text-align: left;"><span style="font-family: courier;">63,Wes,Dey,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">64,Nea,Tan,Computing</span></div><div style="text-align: left;"><span style="font-family: courier;">65,Ava,Day,Nursing</span></div><div style="text-align: left;"><span style="font-family: courier;">66,Rie,Ray,Business</span></div><div style="text-align: left;"><span style="font-family: courier;">67,Ken,Sim,Nursing</span></div></blockquote><p>Add the following code inside the <i>ApplicationDbContext</i> class located inside the <i>Data</i> folder:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public DbSet<Student> Students => Set<Student>();</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">protected override void OnModelCreating(ModelBuilder modelBuilder) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> base.OnModelCreating(modelBuilder);</span></div><div style="text-align: left;"><span style="font-family: courier;"> modelBuilder.Entity<Student>().HasData(LoadStudents());</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span> </div></blockquote><div style="text-align: left;"> </div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// Load students from a csv file named students.csv in the wwwroot folder</span></div><div style="text-align: left;"><span style="font-family: courier;">public static List<Student> LoadStudents() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> var students = new List<Student>();</span></div><div style="text-align: left;"><span style="font-family: courier;"> using (var reader = new StreamReader(Path.Combine("wwwroot", "students.csv"))) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> using var csv = new CsvReader(reader, CultureInfo.InvariantCulture);</span></div><div style="text-align: left;"><span style="font-family: courier;"> students = csv.GetRecords<Student>().ToList();</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> return students;</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p>Let us add a migration and subsequently update the database. Execute the following CLI commands in a terminal window.</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">dotnet ef migrations add Students -o Data/Migrations</span></div><div style="text-align: left;"><span style="font-family: courier;">dotnet ef database update</span></div></blockquote><p>At this point the database and tables are created in a SQLite database named app.db.</p><h2 style="text-align: left;">Helper Methods</h2><p>We need a couple of static helper methods to assist us along the way. In the <i>Models</i> folder, add a class named <i>Utils</i> and add to it the following class definition:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public class Utils {</span></div><div style="text-align: left;"><span style="font-family: courier;"> public static string GetConfigValue(string config) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> IConfigurationBuilder builder = new ConfigurationBuilder();</span></div><div style="text-align: left;"><span style="font-family: courier;"> if (System.IO.File.Exists("appsettings.json"))</span></div><div style="text-align: left;"><span style="font-family: courier;"> builder.AddJsonFile("appsettings.json", false, true);</span></div><div style="text-align: left;"><span style="font-family: courier;"> if (System.IO.File.Exists("appsettings.Development.json"))</span></div><div style="text-align: left;"><span style="font-family: courier;"> builder.AddJsonFile("appsettings.Development.json", false, true);</span></div><div style="text-align: left;"><span style="font-family: courier;"> IConfigurationRoot root = builder.Build();</span></div><div style="text-align: left;"><span style="font-family: courier;"> return root[config]!;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div></blockquote><div style="text-align: left;"> </div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public static ApplicationDbContext GetDbContext() {<br /></span><span style="font-family: courier;"> var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();<br /></span><span style="font-family: courier;"> var connStr = Utils.GetConfigValue("ConnectionStrings:DefaultConnection");<br /></span><span style="font-family: courier;"> optionsBuilder.UseSqlite(connStr);<br /></span><span style="font-family: courier;"> ApplicationDbContext db = new ApplicationDbContext(optionsBuilder.Options);<br /></span><span style="font-family: courier;"> return db;<br /></span><span style="font-family: courier;"> }<br /></span><span style="font-family: courier;">}</span></div></blockquote><p>Method <i>GetConfigValue() </i>will read values in <i>appsettings.json</i> from any static method. The second <i>GetDbContext()</i> method gets an instance of the <i>ApplicationDbContext</i> class, also from any static method.</p><h2 style="text-align: left;">Plugins</h2><p>Create a folder named <i>Plugins</i> and add to it the following class file named <i>StudentPlugin.cs</i> with this code:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><span style="font-family: courier;">public class StudentPlugin {</span></div><div><span style="font-family: courier;"> [KernelFunction, Description("Get student details by first name and last name")]</span></div><div><span style="font-family: courier;"> public static string? GetStudentDetails(</span></div><div><span style="white-space: normal;"><span style="font-family: courier;"><span style="white-space: pre;"> </span> [Description("student first name, e.g. Kim")]</span></span></div><div><span style="white-space: normal;"><span style="font-family: courier;"><span style="white-space: pre;"> </span> string firstName,</span></span></div><div><span style="white-space: normal;"><span style="font-family: courier;"><span style="white-space: pre;"> </span> [Description("student last name, e.g. Ash")]</span></span></div><div><span style="white-space: normal;"><span style="font-family: courier;"><span style="white-space: pre;"> </span> string lastName</span></span></div><div><span style="font-family: courier;"> ) {</span></div><div><span style="font-family: courier;"> var db = Utils.GetDbContext();</span></div><div><span style="font-family: courier;"> var studentDetails = db.Students</span></div><div><span style="font-family: courier;"> .Where(s => s.FirstName == firstName && s.LastName == lastName).FirstOrDefault();</span></div><div><span style="font-family: courier;"> if (studentDetails == null)</span></div><div><span style="font-family: courier;"> return null;</span></div><div><span style="font-family: courier;"> return studentDetails.ToString();</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> [KernelFunction, Description("Get students in a school given the school name")]</span></div><div><span style="font-family: courier;"> public static string? GetStudentsBySchool(</span></div><div><span style="font-family: courier;"> [Description("The school name, e.g. Nursing")]</span></div><div><span style="font-family: courier;"> string school</span></div><div><span style="font-family: courier;"> ) {</span></div><div><span style="font-family: courier;"> var studentsBySchool = Utils.GetDbContext().Students</span></div><div><span style="font-family: courier;"> .Where(s => s.School == school).ToList();</span></div><div><span style="font-family: courier;"> if (studentsBySchool.Count == 0)</span></div><div><span style="font-family: courier;"> return null;</span></div><div><span style="font-family: courier;"> return JsonSerializer.Serialize(studentsBySchool);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> [KernelFunction, Description("Get the school with most or least students. Takes boolean argument with true for most and false for least.")]</span></div><div><span style="font-family: courier;"> static public string? GetSchoolWithMostOrLeastStudents(</span></div><div><span style="font-family: courier;"> [Description("isMost is a boolean argument with true for most and false for least. Default is true.")]</span></div><div><span style="font-family: courier;"> bool isMost = true</span></div><div><span style="font-family: courier;"> ) {</span></div><div><span style="font-family: courier;"> var students = Utils.GetDbContext().Students.ToList();</span></div><div><span style="font-family: courier;"> IGrouping<string, Student>? schoolGroup = null;</span></div><div><span style="font-family: courier;"> if (isMost)</span></div><div><span style="font-family: courier;"> schoolGroup = students.GroupBy(s => s.School)</span></div><div><span style="font-family: courier;"> .OrderByDescending(g => g.Count()).FirstOrDefault()!;</span></div><div><span style="font-family: courier;"> else</span></div><div><span style="font-family: courier;"> schoolGroup = students.GroupBy(s => s.School)</span></div><div><span style="font-family: courier;"> .OrderBy(g => g.Count()).FirstOrDefault()!;</span></div><div><span style="font-family: courier;"> if (schoolGroup != null)</span></div><div><span style="font-family: courier;"> return $"{schoolGroup.Key} has {schoolGroup.Count()} students";</span></div><div><span style="font-family: courier;"> else</span></div><div><span style="font-family: courier;"> return null;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> [KernelFunction, Description("Get students grouped by school.")]</span></div><div><span style="font-family: courier;"> static public string? GetStudentsInSchool() {</span></div><div><span style="font-family: courier;"> var students = Utils.GetDbContext().Students.ToList().GroupBy(s => s.School)</span></div><div><span style="font-family: courier;"> .OrderByDescending(g => g.Count());</span></div><div><span style="font-family: courier;"> if (students == null)</span></div><div><span style="font-family: courier;"> return null;</span></div><div><span style="font-family: courier;"> else</span></div><div><span style="font-family: courier;"> return JsonSerializer.Serialize(students);</span></div><div><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p> In the above code, there are four methods with these purposes:</p>
<table border="1">
<tbody><tr><td>GetStudentDetails()</td><td>Gets student details given first and last names</td></tr>
<tr><td>GetStudentsBySchool()</td><td>Gets students in a school given the name of the school</td></tr>
<tr><td>GetSchoolWithMostOrLeastStudents()</td><td>Takes a Boolean value <i>isMost </i>– true returns school with most students and false returns school with least students.</td></tr>
<tr><td>GetStudentsInSchool()</td><td>Takes no arguments and returns a count of students by school.</td></tr>
</tbody></table>
<h2 style="text-align: left;">The User Interface</h2><p>We will re-purpose the <i>Index.cshtml </i>and <i>Index.cshtml.cs</i> files so the user can enter a prompt in natural language and receive a response that comes from the OpenAI model working with our semantic kernel plugin. </p><h3 style="text-align: left;">Index.chtml</h3><p>Replace the content of <i>Pages/Index.cshtml</i> with:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">@page</span></div><div style="text-align: left;"><span style="font-family: courier;">@model IndexModel</span></div><div style="text-align: left;"><span style="font-family: courier;">@{</span></div><div style="text-align: left;"><span style="font-family: courier;"> ViewData["Title"] = Model.Service + " Function Calling with Semantic Kernel";</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div><div style="text-align: left;"><span style="font-family: courier;"><div class="text-center"></span></div><div style="text-align: left;"><span style="font-family: courier;"> <h3 class="display-6">@ViewData["Title"]</h3></span></div><div style="text-align: left;"><span style="font-family: courier;"> <form method="post"></span></div><div style="text-align: left;"><span style="font-family: courier;"> <input type="text" name="prompt" size="80" required /></span></div><div style="text-align: left;"><span style="font-family: courier;"> <input type="submit" value="Submit" /></span></div><div style="text-align: left;"><span style="font-family: courier;"> </form></span></div><div style="text-align: left;"><span style="font-family: courier;"> <div style="text-align: left"></span></div><div style="text-align: left;"><span style="font-family: courier;"> <h5>Example prompts:</h5></span></div><div style="text-align: left;"><span style="font-family: courier;"> <p>Which school does Mat Tan go to?</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> <p>Which school has the most students?</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> <p>Which school has the least students?</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> <p>Get the count of students in each school.</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> <p>How many students are there in the school of Mining?</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> <p>What is the ID of Jan Fry and which school does she go to?</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> <p>Which students belong to the school of Business? Respond only in JSON format.</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> <p>Which students in the school of Nursing have their first or last name start with the letter 'J'?</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> </div></span></div><div style="text-align: left;"><span style="font-family: courier;"> @if (Model.Reply != null)</span></div><div style="text-align: left;"><span style="font-family: courier;"> {</span></div><div style="text-align: left;"><span style="font-family: courier;"> <p class="alert alert-success">@Model.Reply</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"></div></span></div></blockquote><p>The above markup displays an HTML form that accepts a prompt from a user. The prompt is then submitted to the server and the response is displayed in a paragraph (<p> tag) with a green background (Bootstrap class alert-success).</p><p>Meantime, at the bottom of the page there are some suggested prompts – namely:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">Which school does Mat Tan go to?</span></div><div style="text-align: left;"><span style="font-family: courier;">Which school has the most students?</span></div><div style="text-align: left;"><span style="font-family: courier;">Which school has the least students?</span></div><div style="text-align: left;"><span style="font-family: courier;">Get the count of students in each school.</span></div><div style="text-align: left;"><span style="font-family: courier;">How many students are there in the school of Mining?</span></div><div style="text-align: left;"><span style="font-family: courier;">What is the ID of Jan Fry and which school does she go to?</span></div><div style="text-align: left;"><span style="font-family: courier;">Which students belong to the school of Business? Respond only in JSON format.</span></div><div style="text-align: left;"><span style="font-family: courier;">Which students in the school of Nursing have their first or last name start with the letter 'J'?</span></div></blockquote><h3 style="text-align: left;">Index.chtml.cs</h3><p>Replace the <i>IndexModel</i> class definition in <i>Pages/Index.cshtml.cs</i> with:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public class IndexModel : PageModel {</span></div><div style="text-align: left;"><span style="font-family: courier;"> private readonly ILogger<IndexModel> _logger;</span></div><div style="text-align: left;"><span style="font-family: courier;"> private readonly IConfiguration _config;</span></div></blockquote><div style="text-align: left;"> </div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> [BindProperty]</span></div><div style="text-align: left;"><span style="font-family: courier;"> public string? Reply { get; set; }</span></div></blockquote><div style="text-align: left;"> </div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> [BindProperty]</span></div><div style="text-align: left;"><span style="font-family: courier;"> public string? Service { get; set; }</span></div></blockquote><div style="text-align: left;"> </div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public IndexModel(ILogger<IndexModel> logger, IConfiguration config) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> _logger = logger;</span></div><div style="text-align: left;"><span style="font-family: courier;"> _config = config;</span></div><div style="text-align: left;"><span style="font-family: courier;"> Service = _config["AIService"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> public void OnGet() { }</span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="color: #38761d;">// action method that receives prompt from the form</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> public async Task<IActionResult> OnPostAsync(string prompt) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="color: #38761d;">// call the Azure Function</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> var response = await CallFunction(prompt);</span></div><div style="text-align: left;"><span style="font-family: courier;"> Reply = response;</span></div><div style="text-align: left;"><span style="font-family: courier;"> return Page();</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div></blockquote><div style="text-align: left;"> </div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> private async Task<string> CallFunction(string question) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> string azEndpoint = _config["AzureOpenAiSettings:Endpoint"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;"> string azApiKey = _config["AzureOpenAiSettings:ApiKey"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;"> string azModel = _config["AzureOpenAiSettings:Model"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;"> string oaiModelType = _config["OpenAiSettings:ModelType"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;"> string oaiApiKey = _config["OpenAiSettings:ApiKey"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;"> string oaiModel = _config["OpenAiSettings:Model"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;"> string oaiOrganization = _config["OpenAiSettings:Organization"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;"> var builder = Kernel.CreateBuilder();</span></div><div style="text-align: left;"><span style="font-family: courier;"> if (Service!.ToLower() == "openai")</span></div><div style="text-align: left;"><span style="font-family: courier;"> builder.Services.AddOpenAIChatCompletion(oaiModelType, oaiApiKey);</span></div><div style="text-align: left;"><span style="font-family: courier;"> else</span></div><div style="text-align: left;"><span style="font-family: courier;"> builder.Services.AddAzureOpenAIChatCompletion(azModel, azEndpoint, azApiKey);</span></div><div style="text-align: left;"><span style="font-family: courier;"> builder.Services.AddLogging(c => c.AddDebug().SetMinimumLevel(LogLevel.Trace));</span></div><div style="text-align: left;"><span style="font-family: courier;"> builder.Plugins.AddFromType<StudentPlugin>();</span></div><div style="text-align: left;"><span style="font-family: courier;"> var kernel = builder.Build();</span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="color: #38761d;">// Create chat history</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> ChatHistory history = [];</span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="color: #38761d;">// Get chat completion service</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();</span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="color: #38761d;">// Get user input</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> history.AddUserMessage(question);</span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="color: #38761d;">// Enable auto function calling</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions</span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="color: #38761d;">// Get the response from the AI</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> var result = chatCompletionService.GetStreamingChatMessageContentsAsync(</span></div><div style="text-align: left;"><span style="font-family: courier;"> history,</span></div><div style="text-align: left;"><span style="font-family: courier;"> executionSettings: openAIPromptExecutionSettings,</span></div><div style="text-align: left;"><span style="font-family: courier;"> kernel: kernel);</span></div><div style="text-align: left;"><span style="font-family: courier;"> string fullMessage = "";</span></div><div style="text-align: left;"><span style="font-family: courier;"> await foreach (var content in result) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> fullMessage += content.Content;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="color: #38761d;">// Add the message to the chat history</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> history.AddAssistantMessage(fullMessage);</span></div><div style="text-align: left;"><span style="font-family: courier;"> return fullMessage;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p style="text-align: left;">In the above code, the prompt entered by the user is posted to the <i>OnPostAsync() </i>method. The prompt is then passed to the <i>CallFunction() </i>method, which returns the final response from Azure OpenAI.</p><p>The <i>CallFunction()</i> method reads the OpenAI or Azure OpenAI settings from <i>appsettings.json</i>, depending on the <i>AIService</i> key.</p><p>A builder object is created from Semantic<i> </i>Kernel. If we are using OpenAI, then the <i>AddOpenAIChatCompletion</i> service is added. Otherwise, the <i>AddAzureOpenAIChatCompletion</i> service is added.</p><p>The <i>StudentPlugin</i> is then added to the builder object <i>Plugins</i> collection.</p><p>The builder <i>Build()</i> method is then called returning a kernel object. From the kernel object we then get a <i>chatCompletionService</i> object by calling the <i>GetRequiredService()</i> method.</p><p>Thereafter:</p><p></p><ul style="text-align: left;"><li>Add the prompt to the history</li><li>Make a call to the chat message service and receive a response</li><li>Concatenate response into a single string</li><li>Return the concatenated message</li></ul><p></p><h2 style="text-align: left;">Trying the application</h2><p>In a terminal window, at the root of the Razor Pages web application, enter the following command:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></p><p>The following page will display in your default browser:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEix9Tat8AcaxmdCKq5LI46u-Kk7zWIDlTXZIhDp78diUIib23Rjz8m2hhJ4uFyWJSfGAur9loIKd81rP_YE5gTns_4-lfYLUcQ_FZWjKgHMdiVOOS3e8cd4Fv4A4PCpbyrMJa2P-3pvPcba3dqfXyDOxb9wViibIZGgppkrVWeZmhms0z7PoGA1lomSoaG7" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="697" data-original-width="1113" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEix9Tat8AcaxmdCKq5LI46u-Kk7zWIDlTXZIhDp78diUIib23Rjz8m2hhJ4uFyWJSfGAur9loIKd81rP_YE5gTns_4-lfYLUcQ_FZWjKgHMdiVOOS3e8cd4Fv4A4PCpbyrMJa2P-3pvPcba3dqfXyDOxb9wViibIZGgppkrVWeZmhms0z7PoGA1lomSoaG7=w640-h400" width="640" /></a></div><br />You can enter any of the suggested prompts to ensure we are getting the proper results. I entered the last prompt and got these results:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhuvS8d1OdGuSadr0CVLTc9bVn9RIT5myb7U8U2rjbcR8glY6iVWCBwLKfePzROXR9GkkMQVeVI2Wyb2R-jgbZ9Zp9YSyPrOB5kgv3-C2YQTIWjhyZOC9CtKHI_jy3XN4IcUT-RJVvJFik5ql5Pe8kZemsFd5VViTtOTl8PYjhIAr7GO1BD8j4FPuPEVw8X" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="110" data-original-width="874" height="80" src="https://blogger.googleusercontent.com/img/a/AVvXsEhuvS8d1OdGuSadr0CVLTc9bVn9RIT5myb7U8U2rjbcR8glY6iVWCBwLKfePzROXR9GkkMQVeVI2Wyb2R-jgbZ9Zp9YSyPrOB5kgv3-C2YQTIWjhyZOC9CtKHI_jy3XN4IcUT-RJVvJFik5ql5Pe8kZemsFd5VViTtOTl8PYjhIAr7GO1BD8j4FPuPEVw8X=w640-h80" width="640" /></a></div><br /><hr /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjs_2705PElUav0Anr_vYRTPSvs364fSoYGmB9pEybkrR55wO-uxC59FBSOMydD5H7QIA3VdyGY5YdP-gwmW0h7VWLwP8j9luUagGSzfs--KGvFQh-dpiymunMWp9l4YkeBF42Vuseg-91R8mADCzxRhMW9M-R1GHIJ2o31HsVlkCqlH1Q3Sa3BQlW2t8PL" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="94" data-original-width="1263" height="48" src="https://blogger.googleusercontent.com/img/a/AVvXsEjs_2705PElUav0Anr_vYRTPSvs364fSoYGmB9pEybkrR55wO-uxC59FBSOMydD5H7QIA3VdyGY5YdP-gwmW0h7VWLwP8j9luUagGSzfs--KGvFQh-dpiymunMWp9l4YkeBF42Vuseg-91R8mADCzxRhMW9M-R1GHIJ2o31HsVlkCqlH1Q3Sa3BQlW2t8PL=w640-h48" width="640" /></a></div><h2 style="text-align: left;">Conclusion</h2><div>We have seen how Semantic Kernel and Function Calling can be used with data coming from a database. In this example we are using SQLite. However, an other database source can be used using the same technique.</div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-77548230199756974622024-02-15T08:55:00.000-08:002024-02-19T18:25:05.006-08:00Azure OpenAI Function Calling - a practical example using C# and ASP.NET Razor Pages<p>In as much as one can obtain valuable information by prompting the various OpenAI language models, it becomes even more valuable when these models can be integrated with custom systems and tools. In this demo, we will integrate an LLM with the <i>Azure OpenAI Function Calling</i> capability to query local data in the form of a <i>products.csv</i> file. Of course, this concept can easily be extended to more complex systems and tools. The sample application is based on ASP.NET Razor Pages. It receives a natural language prompt from the user and goes through this three-step process before responding:</p><p></p><ol style="text-align: left;"><li>A call is made to a chat completions API with function definitions and the user’s prompt.</li><li>The model’s response initiates calls to a custom function</li><li>The chat completion API is called again with the response from the custom function, resulting in a final response.</li></ol><p></p><p>Source Code: <a href="https://github.com/medhatelmasry/OaiFuncCall">https://github.com/medhatelmasry/OaiFuncCall</a></p><p>Companion Video: <a href="https://youtu.be/3yyq3GWIj4o">https://youtu.be/3yyq3GWIj4o</a></p><h2 style="text-align: left;">Getting Started</h2><p>Let’s start by creating an ASP.NET Razor pages web application. Select a suitable working folder on your computer, then enter the following terminal window commands:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">dotnet new razor -o OaiFuncCall</span></div><div style="text-align: left;"><span style="font-family: courier;">cd OaiFuncCall</span></div></blockquote><p>Add these packages:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">dotnet add package CsvHelper</span></div><div style="text-align: left;"><span style="font-family: courier;">dotnet add package Azure.AI.OpenAI -v 1.0.0-beta.13</span></div></blockquote><p>The <i>CsvHelper</i> package will help us load a list of products from a CSV file named <i>products.csv</i> and hydrate a list of <i>Product</i> objects. The second package is needed to work with Azure OpenAI.</p><h2 style="text-align: left;">Let’s Code</h2><h3 style="text-align: left;">appsettings.json</h3><p>Add this to <i>appsettings.json</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">"AzureOpenAiSettings": {</span></div><div style="text-align: left;"><span style="font-family: courier;"> "Endpoint": "https://YOUR_RESOURCE_NAME.openai.azure.com/",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "Model": "gpt-35-turbo-16k",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "ApiKey": "fake-key-fake-key-fake-key-fake-key"</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p>Of course, you need to adjust the endpoint setting with the appropriate value that pertains to the Azure OpenAI service that you created. Also, enter the correct value for the <i>ApiKey</i>.</p><h3 style="text-align: left;">products.csv</h3><p>Create a text file named <i>products.csv</i> in the <i>wwwroot</i> folder. Copy some sample data from <a href="https://gist.github.com/medhatelmasry/b250023f3b4b5b14713cfc5165f1d030">https://gist.github.com/medhatelmasry/b250023f3b4b5b14713cfc5165f1d030</a> and paste it into your <i>products.csv </i>file. </p><p>Contents of <i>products.csv</i> looks like this:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">ProductId,ProductName,UnitsInStock,UnitPrice</span></div><div style="text-align: left;"><span style="font-family: courier;">1,Aniseed Syrup,39,18</span></div><div style="text-align: left;"><span style="font-family: courier;">2,Chef Anton's Cajun Seasoning,17,19</span></div><div style="text-align: left;"><span style="font-family: courier;">3,Chef Anton's Gumbo Mix,13,10</span></div><div style="text-align: left;"><span style="font-family: courier;">4,Grandma's Boysenberry Spread,53,22</span></div><div style="text-align: left;"><span style="font-family: courier;">5,Uncle Bob's Organic Dried Pears,0,21.35</span></div><div style="text-align: left;"><span style="font-family: courier;">6,Northwoods Cranberry Sauce,120,25</span></div><div style="text-align: left;"><span style="font-family: courier;">7,Mishi Kobe Niku,15,30</span></div><div style="text-align: left;"><span style="font-family: courier;">8,Ikura,6,40</span></div><div style="text-align: left;"><span style="font-family: courier;">9,Queso Cabrales,29,97</span></div><div style="text-align: left;"><span style="font-family: courier;">10,Queso Manchego La Pastora,31,31</span></div><div style="text-align: left;"><span style="font-family: courier;">11,Konbu,22,21</span></div><div style="text-align: left;"><span style="font-family: courier;">12,Tofu,86,38</span></div><div style="text-align: left;"><span style="font-family: courier;">13,Genen Shouyu,24,6</span></div><div style="text-align: left;"><span style="font-family: courier;">14,Pavlova,35,23.25</span></div><div style="text-align: left;"><span style="font-family: courier;">15,Alice Mutton,39,15.5</span></div><div style="text-align: left;"><span style="font-family: courier;">16,Carnarvon Tigers,29,17.45</span></div><div style="text-align: left;"><span style="font-family: courier;">17,Teatime Chocolate Biscuits,0,39</span></div><div style="text-align: left;"><span style="font-family: courier;">18,Sir Rodney's Marmalade,42,62.5</span></div><div style="text-align: left;"><span style="font-family: courier;">19,Sir Rodney's Scones,25,9.2</span></div><div style="text-align: left;"><span style="font-family: courier;">20,Gustaf's KnŠckebršd,40,81</span></div><div style="text-align: left;"><span style="font-family: courier;">21,Tunnbršd,3,10</span></div><div style="text-align: left;"><span style="font-family: courier;">22,Guaran‡ Fant‡stica,104,21</span></div><div style="text-align: left;"><span style="font-family: courier;">23,NuNuCa Nu§-Nougat-Creme,61,9</span></div><div style="text-align: left;"><span style="font-family: courier;">24,GumbŠr GummibŠrchen,20,4.5</span></div><div style="text-align: left;"><span style="font-family: courier;">25,Schoggi Schokolade,76,14</span></div><div style="text-align: left;"><span style="font-family: courier;">26,Ršssle Sauerkraut,15,31.23</span></div><div style="text-align: left;"><span style="font-family: courier;">27,ThŸringer Rostbratwurst,49,43.9</span></div><div style="text-align: left;"><span style="font-family: courier;">28,Nord-Ost Matjeshering,26,45.6</span></div><div style="text-align: left;"><span style="font-family: courier;">29,Gorgonzola Telino,0,123.79</span></div><div style="text-align: left;"><span style="font-family: courier;">30,Mascarpone Fabioli,10,25.89</span></div><div style="text-align: left;"><span style="font-family: courier;">31,Geitost,0,12.5</span></div><div style="text-align: left;"><span style="font-family: courier;">32,Sasquatch Ale,9,32</span></div><div style="text-align: left;"><span style="font-family: courier;">33,Steeleye Stout,112,2.5</span></div><div style="text-align: left;"><span style="font-family: courier;">34,Inlagd Sill,111,14</span></div><div style="text-align: left;"><span style="font-family: courier;">35,Gravad lax,20,18</span></div><div style="text-align: left;"><span style="font-family: courier;">36,C™te de Blaye,112,19</span></div><div style="text-align: left;"><span style="font-family: courier;">37,Chartreuse verte,11,26</span></div><div style="text-align: left;"><span style="font-family: courier;">38,Boston Crab Meat,17,263.5</span></div><div style="text-align: left;"><span style="font-family: courier;">39,Jack's New England Clam Chowder,69,18</span></div><div style="text-align: left;"><span style="font-family: courier;">40,Singaporean Hokkien Fried Mee,123,18.4</span></div><div style="text-align: left;"><span style="font-family: courier;">41,Ipoh Coffee,85,9.65</span></div><div style="text-align: left;"><span style="font-family: courier;">42,Gula Malacca,26,14</span></div><div style="text-align: left;"><span style="font-family: courier;">43,Rogede sild,17,46</span></div><div style="text-align: left;"><span style="font-family: courier;">44,Spegesild,27,19.45</span></div><div style="text-align: left;"><span style="font-family: courier;">45,Zaanse koeken,5,9.5</span></div><div style="text-align: left;"><span style="font-family: courier;">46,Chocolade,95,12</span></div><div style="text-align: left;"><span style="font-family: courier;">47,Maxilaku,36,9.5</span></div><div style="text-align: left;"><span style="font-family: courier;">48,Valkoinen suklaa,15,12.75</span></div><div style="text-align: left;"><span style="font-family: courier;">49,Manjimup Dried Apples,10,20</span></div><div style="text-align: left;"><span style="font-family: courier;">50,Filo Mix,65,16.25</span></div><div style="text-align: left;"><span style="font-family: courier;">51,Perth Pasties,20,53</span></div><div style="text-align: left;"><span style="font-family: courier;">52,Tourtire,38,7</span></div><div style="text-align: left;"><span style="font-family: courier;">53,P‰tŽ chinois,0,32.8</span></div><div style="text-align: left;"><span style="font-family: courier;">54,Gnocchi di nonna Alice,21,7.45</span></div><div style="text-align: left;"><span style="font-family: courier;">55,Ravioli Angelo,115,24</span></div><div style="text-align: left;"><span style="font-family: courier;">56,Escargots de Bourgogne,21,38</span></div><div style="text-align: left;"><span style="font-family: courier;">57,Raclette Courdavault,36,19.5</span></div><div style="text-align: left;"><span style="font-family: courier;">58,Camembert Pierrot,62,13.25</span></div><div style="text-align: left;"><span style="font-family: courier;">59,Sirop d'Žrable,79,55</span></div><div style="text-align: left;"><span style="font-family: courier;">60,Tarte au sucre,19,34</span></div><div style="text-align: left;"><span style="font-family: courier;">61,Vegie-spread,113,28.5</span></div><div style="text-align: left;"><span style="font-family: courier;">62,Wimmers gute Semmelknšdel,17,49.3</span></div><div style="text-align: left;"><span style="font-family: courier;">63,Louisiana Fiery Hot Pepper Sauce,24,43.9</span></div><div style="text-align: left;"><span style="font-family: courier;">64,Louisiana Hot Spiced Okra,22,33.25</span></div><div style="text-align: left;"><span style="font-family: courier;">65,Laughing Lumberjack Lager,76,21.05</span></div><div style="text-align: left;"><span style="font-family: courier;">66,Scottish Longbreads,4,17</span></div><div style="text-align: left;"><span style="font-family: courier;">67,Gudbrandsdalsost,52,14</span></div><div style="text-align: left;"><span style="font-family: courier;">68,Outback Lager,6,12.5</span></div><div style="text-align: left;"><span style="font-family: courier;">69,Flotemysost,26,36</span></div><div style="text-align: left;"><span style="font-family: courier;">70,Mozzarella di Giovanni,15,15</span></div><div style="text-align: left;"><span style="font-family: courier;">71,Ršd Kaviar,26,21.5</span></div><div style="text-align: left;"><span style="font-family: courier;">72,Longlife Tofu,14,34.8</span></div><div style="text-align: left;"><span style="font-family: courier;">73,RhšnbrŠu Klosterbier,101,15</span></div><div style="text-align: left;"><span style="font-family: courier;">74,Lakkalikššri,4,10</span></div><div style="text-align: left;"><span style="font-family: courier;">75,Original Frankfurter grŸne So§e,125,7.75</span></div></blockquote><p>Note that the above data was taken from the <i>Northwind</i> database that used to come with early versions of SQL Server.</p><h2 style="text-align: left;">Function Definitions</h2><p>To demonstrate the power of Azure OpenAI Function Calling, we will create two separate function definitions. The first is a class named <i>ProductAgent</i>, which returns details about a product given the ‘Product Name’. The second is a class named <i>MostExpensiveProductAgent</i>, which returns the most expensive product.</p><p>Create a folder named <i>Models</i> and add to it three C# classes, namely: <i>Product</i>, <i>ProductAgent</i> and <i>MostExpensiveProductAgent</i>.</p><h3 style="text-align: left;">Product.cs</h3><p>Add the following class to <i>Product.cs</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;">p<span style="font-family: courier;">ublic class Product{<br /></span></div><div style="text-align: left;"><span style="font-family: courier;"> public int ProductId { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"> public string? ProductName { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"> public int UnitsInStock { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"> public float UnitPrice { get; set; }</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;"> // Load products from a csv file named products.csv in the wwwroot folder</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> public static List<Product> LoadProducts() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> var products = new List<Product>();</span></div><div style="text-align: left;"><span style="font-family: courier;"> using (var reader = new StreamReader(Path.Combine("wwwroot", "products.csv"))) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> products = csv.GetRecords<Product>().ToList();</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> return products;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public override string ToString() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> return $"Product ID: {ProductId}, Product Name: {ProductName}, Units In Stock: {UnitsInStock}, Unit Price: {UnitPrice}";</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div><p style="text-align: left;"><span style="font-family: courier;"></span></p></blockquote><p><span style="font-family: courier;">T</span>he above code declares a class named <i>Product</i>. The class properties match the column names in the <i>products.csv</i> file. The <i>LoadProducts()</i> static method reads contents of the CSV file and returns a hydrated list of <i>Product</i> objects. Note that the <i>Product</i> class also has a <i>ToString() </i>method.</p><h3 style="text-align: left;">ProductAgent.cs</h3><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public class ProductAgent {</span></div><div style="text-align: left;"><span style="font-family: courier;"> static public string Name = "get_product_details";</span></div><div style="text-align: left;"><span style="font-family: courier;"> static private List<Product> products = Product.LoadProducts(); </span></div><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;"> // Return the function metadata</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> static public FunctionDefinition GetFunctionDefinition() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> return new FunctionDefinition() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> Name = Name,</span></div><div style="text-align: left;"><span style="font-family: courier;"> Description = "Get product details by product name",</span></div><div style="text-align: left;"><span style="font-family: courier;"> Parameters = BinaryData.FromObjectAsJson(</span></div><div style="text-align: left;"><span style="font-family: courier;"> new{</span></div><div style="text-align: left;"><span style="font-family: courier;"> Type = "object",</span></div><div style="text-align: left;"><span style="font-family: courier;"> Properties = new {</span></div><div style="text-align: left;"><span style="font-family: courier;"> ProductName = new {</span></div><div style="text-align: left;"><span style="font-family: courier;"> Type = "string",</span></div><div style="text-align: left;"><span style="font-family: courier;"> Description = "The product name, e.g. Pavlova",</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> },</span></div><div style="text-align: left;"><span style="font-family: courier;"> Required = new[] { "productName" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }),</span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> static public string? GetProductDetails(string product){</span></div><div style="text-align: left;"><span style="font-family: courier;"> var productDetails = products.Where(p => p.ProductName == product).FirstOrDefault();</span></div><div style="text-align: left;"><span style="font-family: courier;"> if (productDetails == null){</span></div><div style="text-align: left;"><span style="font-family: courier;"> return null;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> return productDetails.ToString();</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// Argument for the function</span></div><div style="text-align: left;"><span style="font-family: courier;">public class ProductInput {</span></div><div style="text-align: left;"><span style="font-family: courier;"> public string ProductName { get; set; } = string.Empty;</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p style="text-align: left;"><span style="font-family: courier;">Th</span>e above code does the following:</p><p></p><ul style="text-align: left;"><li>The function definition is created as a JSON object with information about the function name, description, and required parameters.</li><li>The <i>GetProductDetails() </i>method receives a <i>product</i> parameter (essentially <i>ProductName</i>) and returns a string with product details.</li><li>The <i>ProductInput</i> class exists in the <i>ProductAgent.c</i>s file. It will be later used as the argument that is passed to the <i>GetProductDetails() </i>method.</li></ul><p></p><h3 style="text-align: left;">MostExpensiveProductAgent.cs</h3><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public class MostExpensiveProductAgent {</span></div><div style="text-align: left;"><span style="font-family: courier;"> static public string Name = "get_most_expensive_product";</span></div><div style="text-align: left;"><span style="font-family: courier;"> static private List<Product> products = Product.LoadProducts(); </span></div><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;"> // Return the function metadata</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> static public FunctionDefinition GetFunctionDefinition() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> return new FunctionDefinition() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> Name = Name,</span></div><div style="text-align: left;"><span style="font-family: courier;"> Description = "Get details of the most expensive product",</span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> static public string? GetMostExpensiveProductDetails() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> var productDetails = products.OrderByDescending(p => p.UnitPrice).FirstOrDefault();</span></div><div style="text-align: left;"><span style="font-family: courier;"> if (productDetails == null) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> return null;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> return productDetails.ToString();</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><span style="font-family: courier;"><br />T</span>he above code does the following:<br /><p></p><ul style="text-align: left;"><li>Just as with the previous <i>ProductAgent</i> class, the function definition is created as a JSON object with information about the function name, description, and required parameters.</li><li> The <i>GetMostExpensiveProductDetails()</i> method takes no arguments and simply returns a string with details of the most expensive product.</li></ul><p></p><h2 style="text-align: left;">The User Interface</h2><p>We will re-purpose the <i>Index.cshtml</i> and <i>Index.cshtml.cs</i> files so the user can enter a prompt in natural language and receive a response that comes from the OpenAI model working with our custom function. </p><h3 style="text-align: left;">Index.chtml</h3><p>Replace the content of <i>Pages/Index.cshtml</i> with:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">@page</span></div><div style="text-align: left;"><span style="font-family: courier;">@model IndexModel</span></div><div style="text-align: left;"><span style="font-family: courier;">@{</span></div><div style="text-align: left;"><span style="font-family: courier;"> ViewData["Title"] = "OpenAI Function Calling";</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div><div style="text-align: left;"><span style="font-family: courier;"><div class="text-center"></span></div><div style="text-align: left;"><span style="font-family: courier;"> <h1 class="display-4">@ViewData["Title"]</h1></span></div><div style="text-align: left;"><span style="font-family: courier;"> <form method="post"></span></div><div style="text-align: left;"><span style="font-family: courier;"> <input type="text" name="prompt" size="80" required/></span></div><div style="text-align: left;"><span style="font-family: courier;"> <input type="submit" value="Submit" /></span></div><div style="text-align: left;"><span style="font-family: courier;"> </form></span></div><div style="text-align: left;"><span style="font-family: courier;"> <pre style="text-align: left"></span></div><div style="text-align: left;"><span style="font-family: courier;"> </span></div><div style="text-align: left;"><span style="font-family: courier;"> Example prompts:</span></div><div style="text-align: left;"><span style="font-family: courier;"> </span></div><div style="text-align: left;"><span style="font-family: courier;"> What is the id number of product: Louisiana Hot Spiced Okra?</span></div><div style="text-align: left;"><span style="font-family: courier;"> What is the unit price of product: Sir Rodney's Marmalade?</span></div><div style="text-align: left;"><span style="font-family: courier;"> How many units in stock for product: Tofu?</span></div><div style="text-align: left;"><span style="font-family: courier;"> What is the most expensive product?</span></div><div style="text-align: left;"><span style="font-family: courier;"> </pre></span></div><div style="text-align: left;"><span style="font-family: courier;"> @if (Model.Reply != null) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> <p class="alert alert-success">@Model.Reply</p></span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"></div></span></div></blockquote><span style="font-family: courier;"><br />T</span>he above markup displays an HTML form that accepts a prompt from a user. The prompt is then submitted to the server and the response is displayed in a paragraph (<i><p></i> tag) with a green background (Bootstrap class <i>alert-success</i>).<br /><p>Meantime, at the bottom of the page there are some suggested prompts – namely:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">What is the id number of product: Louisiana Hot Spiced Okra?</span></div><div style="text-align: left;"><span style="font-family: courier;">What is the unit price of product: Sir Rodney's Marmalade?</span></div><div style="text-align: left;"><span style="font-family: courier;">How many units in stock for product: Tofu?</span></div><div style="text-align: left;"><span style="font-family: courier;">What is the most expensive product?</span></div></blockquote><h3 style="text-align: left;">Index.chtml.cs</h3><p>Replace the <i>IndexModel</i> class definition in <i>Pages/Index.cshtml.cs</i> with:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public class IndexModel : PageModel {</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> private readonly ILogger<IndexModel> _logger;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> private readonly IConfiguration _config;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> [BindProperty]</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public string? Reply { get; set; }</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public IndexModel(ILogger<IndexModel> logger, IConfiguration config) {</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> _logger = logger;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> _config = config;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> }</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public void OnGet() { }</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;"> </span> </div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;"> // action method that receives prompt from the form</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public async Task<IActionResult> OnPostAsync(string prompt) {</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> // call the Azure Function</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> var response = await CallFunction(prompt);</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> Reply = response;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> return Page();</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> }</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> private async Task<string> CallFunction(string question) {</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> string endpoint = _config["AzureOpenAiSettings:Endpoint"]!;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> string apiKey = _config["AzureOpenAiSettings:ApiKey"]!;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> string model = _config["AzureOpenAiSettings:Model"]!;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> Uri openAIUri = new(endpoint);</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;"> </span> </div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;"> // Instantiate OpenAIClient for Azure Open AI.</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> OpenAIClient client = new(openAIUri, new AzureKeyCredential(apiKey));</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> ChatCompletionsOptions chatCompletionsOptions = new();</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> chatCompletionsOptions.DeploymentName = model;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> ChatChoice responseChoice;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> Response<ChatCompletions> responseWithoutStream;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;"> </span> </div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;"> // Add function definitions</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> FunctionDefinition getProductFunctionDefinition = ProductAgent.GetFunctionDefinition();</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> FunctionDefinition getMostExpensiveProductDefinition = MostExpensiveProductAgent.GetFunctionDefinition();</span></div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> chatCompletionsOptions.Functions.Add(getProductFunctionDefinition);</span></div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> chatCompletionsOptions.Functions.Add(getMostExpensiveProductDefinition);</span></div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> chatCompletionsOptions.Messages.Add(</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> new ChatRequestUserMessage(question)</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> );</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> responseWithoutStream =</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> await client.GetChatCompletionsAsync(chatCompletionsOptions);</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> responseChoice = responseWithoutStream.Value.Choices[0];</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> </span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> while (responseChoice.FinishReason!.Value == CompletionsFinishReason.FunctionCall) {</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;"> // Add message as a history.</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> chatCompletionsOptions.Messages.Add(new ChatRequestUserMessage(responseChoice.Message.ToString()));</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> if (responseChoice.Message.FunctionCall.Name == ProductAgent.Name) {</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> string unvalidatedArguments = responseChoice.Message.FunctionCall.Arguments;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> ProductInput input = JsonSerializer.Deserialize<ProductInput>(unvalidatedArguments,</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })!;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> var functionResultData = ProductAgent.GetProductDetails(input.ProductName);</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> var functionResponseMessage = new ChatRequestFunctionMessage(</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> ProductAgent.Name,</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> JsonSerializer.Serialize(</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> functionResultData,</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> chatCompletionsOptions.Messages.Add(functionResponseMessage);</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> } else if (responseChoice.Message.FunctionCall.Name == MostExpensiveProductAgent.Name) {</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> </span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> var functionResultData = MostExpensiveProductAgent.GetMostExpensiveProductDetails();</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> var functionResponseMessage = new ChatRequestFunctionMessage(</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> MostExpensiveProductAgent.Name,</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> JsonSerializer.Serialize(</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> functionResultData,</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> chatCompletionsOptions.Messages.Add(functionResponseMessage);</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> }</span></div></blockquote><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;"> // Call LLM again to generate the response.</span></span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> responseWithoutStream = await client.GetChatCompletionsAsync(chatCompletionsOptions);</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> responseChoice = responseWithoutStream.Value.Choices[0];</span></div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> }</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> return responseChoice.Message.Content;</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> }</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote></blockquote><p style="text-align: left;">In the above code, the prompt entered by the user is posted to the <i>OnPostAsync()</i> method. The prompt is then passed to the <i>CallFunction() method</i>, which returns the final response from Azure OpenAI.</p><p></p><ul style="text-align: left;"><li>The <i>CallFunction()</i> method reads the Azure OpenAI settings from <i>appsettings.json</i>.</li><li>An <i>OpenAIClient</i> class is instantiated.</li><li>An <i>ChatCompletionsOptions</i> class is instantiated and the message property is set with the original prompt from the user.</li><li>Function definitions for <i>ProductAgent</i> and <i>MostExpensiveProductAgent</i> are obtained.</li><li>A <i>GetChatCompletionsAsync</i> call is then made to Azure Open AI. The response involves a call to a local custom function. The service is smart enough to call the correct function based on the context of the prompt. Responses from local custom function calls are added to the history of messages and sent back to OpenAI. Eventually, after no more local custom function calls are required, the final message is returned.</li></ul><p></p><h2 style="text-align: left;">Trying the application</h2><p>In a terminal window, at the root of the Razor Pages web application, enter the following command:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></p><p>The following page will display in your default browser:</p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiKsqf1G-FfuMqfIPJeMuEf339DR8-yOYo29C2eMy3Squftn0e8I-16pdUwe71mSzTrfQ3LFPw5nEXQseMZvEE7qC1DDMiBNaMLQV35jsJUb2X_CzP70caEgOc2Zv_1JNEC9eH4dajelHyTeBwxTo_HwDFcnIY0DShhLkXLF2o6T7hq0-1BN742ktvB4dV3" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="812" data-original-width="2064" height="252" src="https://blogger.googleusercontent.com/img/a/AVvXsEiKsqf1G-FfuMqfIPJeMuEf339DR8-yOYo29C2eMy3Squftn0e8I-16pdUwe71mSzTrfQ3LFPw5nEXQseMZvEE7qC1DDMiBNaMLQV35jsJUb2X_CzP70caEgOc2Zv_1JNEC9eH4dajelHyTeBwxTo_HwDFcnIY0DShhLkXLF2o6T7hq0-1BN742ktvB4dV3=w640-h252" width="640" />
</a></div><p style="text-align: left;">After separately entering the four suggested prompts, you will receive the following responses:</p></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgo3xDiYMUPRp8GSMg666Qq2ctlHCIUqzpPBee5NLbGgRleCbUABSc1oprzv-XC2MC3eFJ90IKu8BY45a3zLBTAjI1-lRRrgIMKzkDT0fK_wocDs5YzInP2BkONiKbfZbrfZTvFpkzgt64RJiJnmyb1rbqkZV2NhrKhB7M6NrQdgYk2e8Q0cyIUKYCBGFZI" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="498" data-original-width="2058" height="154" src="https://blogger.googleusercontent.com/img/a/AVvXsEgo3xDiYMUPRp8GSMg666Qq2ctlHCIUqzpPBee5NLbGgRleCbUABSc1oprzv-XC2MC3eFJ90IKu8BY45a3zLBTAjI1-lRRrgIMKzkDT0fK_wocDs5YzInP2BkONiKbfZbrfZTvFpkzgt64RJiJnmyb1rbqkZV2NhrKhB7M6NrQdgYk2e8Q0cyIUKYCBGFZI=w640-h154" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><b><span style="color: #38761d;">=======================================================================</span></b></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjJ7J1RRfs9SEXiVbbb9KrCTaXNZeArYi_IErSvbK_n2BZJTX5zHL4JiUYXz3pGh8LQlVGoUcEOUXYkML2I7Ef4DKw3-NAT2Gu5pAyKqOCxxUjbJa9j5c5RQYnKsgb-lfyi6iHF43H_0xKNNT_kCzovOMOVyaK1McOI3pjWpV571U3mw7iinkUF7LFJ1CPo" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="514" data-original-width="2064" height="160" src="https://blogger.googleusercontent.com/img/a/AVvXsEjJ7J1RRfs9SEXiVbbb9KrCTaXNZeArYi_IErSvbK_n2BZJTX5zHL4JiUYXz3pGh8LQlVGoUcEOUXYkML2I7Ef4DKw3-NAT2Gu5pAyKqOCxxUjbJa9j5c5RQYnKsgb-lfyi6iHF43H_0xKNNT_kCzovOMOVyaK1McOI3pjWpV571U3mw7iinkUF7LFJ1CPo=w640-h160" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><span style="color: #38761d;"><b>=======================================================================</b></span></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiW1w3U5V-QfX2RpIl-PEJRlI7grhaf47sJf_STA09n8LfibpCe87rYPz6kPHh1-7VQw5Phc2pQr-b-Y9hBBBqyl0FmfdnElU7xzhCCIdCkiZ91_TmcJMX4aotBZbM1gHitJq7TU6_LYEqNT2quIET--4y7cbDX02VJ-uMQ18kQY9PwqtXkGlnIFRpijAml" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="536" data-original-width="2070" height="166" src="https://blogger.googleusercontent.com/img/a/AVvXsEiW1w3U5V-QfX2RpIl-PEJRlI7grhaf47sJf_STA09n8LfibpCe87rYPz6kPHh1-7VQw5Phc2pQr-b-Y9hBBBqyl0FmfdnElU7xzhCCIdCkiZ91_TmcJMX4aotBZbM1gHitJq7TU6_LYEqNT2quIET--4y7cbDX02VJ-uMQ18kQY9PwqtXkGlnIFRpijAml=w640-h166" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><span style="color: #38761d;"><b>=======================================================================</b></span></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgRoQRxAQqlj6ZdWgkRqmWXdnK8lAuMrDFGvKJbrg9UVp9hzt3-HBmZFncGS_9I2nw6VGCDYjm_zHvteKDgHx-XBjWDT1p_UqC3x5342gQOA-QFcVllG8mL09s_Hhibs7Il5Bx9M4QU9zA8_99io6H0C2Zgk5sIRJaUsBlBSS1__Bnd49Wmk4wo_rhbibxG" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="524" data-original-width="2050" height="164" src="https://blogger.googleusercontent.com/img/a/AVvXsEgRoQRxAQqlj6ZdWgkRqmWXdnK8lAuMrDFGvKJbrg9UVp9hzt3-HBmZFncGS_9I2nw6VGCDYjm_zHvteKDgHx-XBjWDT1p_UqC3x5342gQOA-QFcVllG8mL09s_Hhibs7Il5Bx9M4QU9zA8_99io6H0C2Zgk5sIRJaUsBlBSS1__Bnd49Wmk4wo_rhbibxG=w640-h164" width="640" /></a></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;"><h2 style="clear: both; text-align: left;">Conclusion</h2><div class="separator" style="clear: both;">The opportunities that Function Calling opens is enormous. I am simply scratching the surface of what the possibilities are. </div><h2 style="clear: both; text-align: left;">Resources</h2><div class="separator" style="clear: both;"><a href="https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/function-calling-is-now-available-in-azure-openai-service/ba-p/3879241">https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/function-calling-is-now-available-in-azure-openai-service/ba-p/3879241</a></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><a href="https://dev.to/kenakamu/c-azure-open-ai-and-function-calling-h7j">https://dev.to/kenakamu/c-azure-open-ai-and-function-calling-h7j</a></div><div><br /></div></div></div></div></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-2475508608058676412024-02-07T14:15:00.000-08:002024-02-07T14:40:48.864-08:00Base64 images with Azure OpenAI Dall-E 3, Semantic Kernel, and C#<p>We will generate Base64 images using the OpenAI Dall-E 3 service and Semantic Kernel. The Base64 representation of the image will be saved in a text file. Thereafter, we will read the text file from an index.html page using JavaScript and subsequently render the image on a web page.</p><p>Source Code: <a href="https://github.com/medhatelmasry/DalleImageBase64/">https://github.com/medhatelmasry/DalleImageBase64/</a></p><h2><b>What is Semantic Kernel?</b></h2><p>This is the official definition obtained from <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Create AI agents with Semantic Kernel | Microsoft Learn</a>:</p><p><i>Semantic Kernel is an open-source SDK that lets you easily build agents that can call your existing code. As a highly extensible SDK, you can use Semantic Kernel with models from OpenAI, Azure OpenAI, Hugging Face, and more!</i> </p><h2>Getting Started</h2><p>In a suitable directory, create a console application named <i>DalleImageBase64</i> and add to it three packages needed for our application with the following terminal window commands:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier;">dotnet new console -o DalleImageBase64</span></div><div><span style="font-family: courier;">cd </span><span style="font-family: courier;">DalleImageBase64</span></div><div><span style="font-family: courier;">dotnet add package Microsoft.SemanticKernel</span></div><div><span style="font-family: courier;">dotnet add package System.Configuration.ConfigurationManager</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier;">dotnet add package SkiaSharp </span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div> </div></blockquote><p>Create a file named <i>App.config</i> in the root folder of the console application and add to it the important parameters that allow access to the Azure OpenAI service. Contents of <i>App.config</i> are like the following:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><<span style="font-family: courier;">?xml version="1.0"?><br /></span></div><div><span style="font-family: courier;"><configuration></span></div><div><span style="font-family: courier;"> <appSettings></span></div><div><span style="font-family: courier;"> <add key="endpoint" value="https://fake.openai.azure.com/" /></span></div><div><span style="font-family: courier;"> <add key="api-key" value="fakekey-fakekey-fakekey-fakekey" /></span></div><div><span style="font-family: courier;"> <add key="gpt-deployment" value="gpt-35-turbo" /></span></div><div><span style="font-family: courier;"> <add key="dalle-deployment" value="dall-e-3" /></span></div><div><span style="font-family: courier;"> </appSettings></span></div><div><span style="font-family: courier;"></configuration></span></div></blockquote><p>NOTE: Since I cannot share the <i>endpoint</i> and <i>apiKey</i> with you, I have fake values for these settings.</p><p>Currently, the Dall-E 3 model is in preview and only available in the "Sweden Central" Azure data centre according to <a href="https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#dall-e-models-preview">https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#dall-e-models-preview</a>. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgh6r23RC7avzdDjb7Hk89jKldPtAMt8G6KRNNoIUGc38wik6dTq70YBzA66rTPa1t16mWXjosC6jXtOEZDzZZI3CCegKfdQ1DCg7TfyaEeem4uVWQc1ds4UnNzaX5m9KAMRN5XQwfvawWAuaZ_AZOgpISihcuV81juQUrHEg4Q-AI8rYGQ9Js38kleOQmJ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="237" data-original-width="617" height="154" src="https://blogger.googleusercontent.com/img/a/AVvXsEgh6r23RC7avzdDjb7Hk89jKldPtAMt8G6KRNNoIUGc38wik6dTq70YBzA66rTPa1t16mWXjosC6jXtOEZDzZZI3CCegKfdQ1DCg7TfyaEeem4uVWQc1ds4UnNzaX5m9KAMRN5XQwfvawWAuaZ_AZOgpISihcuV81juQUrHEg4Q-AI8rYGQ9Js38kleOQmJ=w400-h154" width="400" /></a></div><p></p><h2>Let's Code</h2><p>Open <i>Program.cs</i> and delete all its contents. Add the following <i>using</i> statements at the top:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">using System.Configuration;</span></div><div style="text-align: left;"><span style="font-family: courier;">using Microsoft.SemanticKernel;</span></div><div style="text-align: left;"><span style="font-family: courier;">using Microsoft.SemanticKernel.Connectors.OpenAI;</span></div><div style="text-align: left;"><span style="font-family: courier;">using Microsoft.SemanticKernel.TextToImage;</span></div></blockquote><p>We need to read the <i>App.config</i> file settings into our application. We will use the <i>ConfigurationManager</i> from namespace <i>System.Configuration</i>. To read settings from <i>App.config</i> with <i>ConfigurationManager</i>, append the following code to <i>Program.cs</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="color: #274e13; font-family: courier;">// Get configuration settings from App.config</span></div><div><span style="font-family: courier;">string _endpoint = ConfigurationManager.AppSettings["endpoint"]!;</span></div><div><span style="font-family: courier;">string _apiKey = ConfigurationManager.AppSettings["api-key"]!;</span></div><div><span style="font-family: courier;">string _dalleDeployment = ConfigurationManager.AppSettings["dalle-deployment"]!;</span></div><div><span style="font-family: courier;">string _gptDeployment = ConfigurationManager.AppSettings["gpt-deployment"]!;</span></div></blockquote><p>Currently, we need to disable certain warning directives by adding the following into the <i>.csproj </i>file inside the <i><PropertyGroup></i> block:</p><p style="text-align: center;"><span style="font-family: courier;"><NoWarn>SKEXP0001, SKEXP0002, SKEXP0011, SKEXP0012</NoWarn></span></p><p>Then, append this code to <i>Program.cs</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="color: #38761d; font-family: courier;">// Create a kernel builder</span></div><div><span style="font-family: courier;">var builder = Kernel.CreateBuilder();</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="color: #38761d; font-family: courier;">// Add OpenAI services to the kernel</span></div><div><span style="font-family: courier;">builder.AddAzureOpenAITextToImage(_dalleDeployment, _endpoint, _apiKey);</span></div><div><span style="font-family: courier;">builder.AddAzureOpenAIChatCompletion(_gptDeployment, _endpoint, _apiKey);</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="color: #38761d; font-family: courier;">// Build the kernel</span></div><div><span style="font-family: courier;">var kernel = builder.Build();</span></div></blockquote><span style="font-family: courier;"><div><span style="font-family: courier;"><br /></span></div>W</span>e created a <i>builder</i> object from <i>SematicKernel</i>, added the <i>AddAzureOpenAITextToImage</i> and <i>AddAzureOpenAIChatCompletion</i> services, then obtained an instance of the <i>kernel</i> object.<br /><p>Get an instance of the "Dall-E" service from the <i>kernel</i> with the following code:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="color: #38761d; font-family: courier;">// Get AI service instance used to generate images</span></div><div><span style="font-family: courier;">var dallE = kernel.GetRequiredService<ITextToImageService>();</span></div></blockquote><p>Let us create a prompt that generates an image representing a phrase entered by the user. Append this code to <i>Program.cs</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="color: #38761d; font-family: courier;">// create execution settings for the prompt</span></div><div><span style="font-family: courier;">var prompt = @"</span></div><div><span style="font-family: courier;">Think about an artificial object that represents {{$input}}.";</span></div></blockquote><p>We then configure the prompt execution settings with:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier;">var executionSettings = new OpenAIPromptExecutionSettings {</span></div><div><span style="font-family: courier;"> MaxTokens = 256,</span></div><div><span style="font-family: courier;"> Temperature = 1</span></div><div><span style="font-family: courier;">};</span></div></blockquote><p><i>Temperature</i> is a measure of how creative you want the AI to be. This ranges from 0 to 1, where 0 is least creative and 1 is most creative.</p><p>We will create a semantic function from our prompt with:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="color: #38761d; font-family: courier;">// create a semantic function from the prompt</span></div><div><span style="font-family: courier;">var genImgFunction = kernel.CreateFunctionFromPrompt(prompt, executionSettings);</span></div></blockquote><p>Let us ask the user for input with this code:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="color: #38761d; font-family: courier;">// Get a phrase from the user</span></div><div><span style="font-family: courier;">Console.WriteLine("Enter a phrase to generate an image from: ");</span></div><div><span style="font-family: courier;">string? phrase = Console.ReadLine();</span></div><div><span style="font-family: courier;">if (string.IsNullOrEmpty(phrase)) {</span></div><div><span style="font-family: courier;"> Console.WriteLine("No phrase entered.");</span></div><div><span style="font-family: courier;"> return;</span></div><div><span style="font-family: courier;">}</span></div></blockquote><p>Next, we will ask the <i>kernel</i> to combine the <i>prompt</i> with the <i>input</i> received from to user.</p><div><span style="font-family: courier;"><span style="color: #38761d;">// Invoke the semantic function to generate an image description</span><br />var imageDescResult = await kernel.InvokeAsync(genImgFunction, new() { ["input"] = phrase });<br />var imageDesc = imageDescResult.ToString();</span></div><p>Finally, ask Dall-E service to do the important work of generating an image based on the description. It returns an image url. This is done with the following code:</p><div><span style="color: #38761d; font-family: courier;">// Use DALL-E 3 to generate an image. <br /></span><span style="font-family: courier;"><span style="color: #38761d;">// In this case, OpenAI returns a URL (though you can ask to return a base64 image)</span><br /></span><span style="font-family: courier;">var imageUrl = await dallE.GenerateImageAsync(imageDesc.Trim(), 1024, 1024);</span></div><p>Let’s print the output URL so that the user can pop it into a browser to see what it looks like:<br /></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// Display the image URL</span> </span></div><div style="text-align: left;"><span style="font-family: courier;">Console.WriteLine($"Image URL:\n\n{imageUrl}"); </span></div></blockquote><p>We will next use the <i>SkiaSharp</i> package (installed earlier on) to save the the image to the computer file system. Create a helper class named <i>SkiaUtils</i> with the following code:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public static class SkiaUtils {</span></div></blockquote><p> <span style="font-family: courier;"> public static async Task<string> SaveImageToFile(string url, int width, int height, string filename = "image.png") {</span></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> SKImageInfo info = new SKImageInfo(width, height);</span></div><div style="text-align: left;"><span style="font-family: courier;"> SKSurface surface = SKSurface.Create(info);</span></div><div style="text-align: left;"><span style="font-family: courier;"> SKCanvas canvas = surface.Canvas;</span></div><div style="text-align: left;"><span style="font-family: courier;"> canvas.Clear(SKColors.White);</span></div><div style="text-align: left;"><span style="font-family: courier;"> var httpClient = new HttpClient();</span></div><div style="text-align: left;"><span style="font-family: courier;"> using (Stream stream = await httpClient.GetStreamAsync(url))</span></div><div style="text-align: left;"><span style="font-family: courier;"> using (MemoryStream memStream = new MemoryStream()) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> await stream.CopyToAsync(memStream);</span></div><div style="text-align: left;"><span style="font-family: courier;"> memStream.Seek(0, SeekOrigin.Begin);</span></div><div style="text-align: left;"><span style="font-family: courier;"> SKBitmap webBitmap = SKBitmap.Decode(memStream);</span></div><div style="text-align: left;"><span style="font-family: courier;"> canvas.DrawBitmap(webBitmap, 0, 0, null);</span></div><div style="text-align: left;"><span style="font-family: courier;"> surface.Draw(canvas, 0, 0, null);</span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100).SaveTo(new FileStream(filename, FileMode.Create));</span></div><div style="text-align: left;"><span style="font-family: courier;"> return filename;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div></blockquote><p> <span style="font-family: courier;"> public static async Task<string> GetImageToBase64String(string url, int width, int height) {</span></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> SKImageInfo info = new SKImageInfo(width, height);</span></div><div style="text-align: left;"><span style="font-family: courier;"> SKSurface surface = SKSurface.Create(info);</span></div><div style="text-align: left;"><span style="font-family: courier;"> SKCanvas canvas = surface.Canvas;</span></div><div style="text-align: left;"><span style="font-family: courier;"> canvas.Clear(SKColors.White);</span></div><div style="text-align: left;"><span style="font-family: courier;"> var httpClient = new HttpClient();</span></div><div style="text-align: left;"><span style="font-family: courier;"> using (Stream stream = await httpClient.GetStreamAsync(url))</span></div><div style="text-align: left;"><span style="font-family: courier;"> using (MemoryStream memStream = new MemoryStream()) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> await stream.CopyToAsync(memStream);</span></div><div style="text-align: left;"><span style="font-family: courier;"> memStream.Seek(0, SeekOrigin.Begin);</span></div><div style="text-align: left;"><span style="font-family: courier;"> SKBitmap webBitmap = SKBitmap.Decode(memStream);</span></div><div style="text-align: left;"><span style="font-family: courier;"> canvas.DrawBitmap(webBitmap, 0, 0, null);</span></div><div style="text-align: left;"><span style="font-family: courier;"> surface.Draw(canvas, 0, 0, null);</span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> using (MemoryStream memStream = new MemoryStream()) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> surface.Snapshot().Encode(SKEncodedImageFormat.Png, 100).SaveTo(memStream);</span></div><div style="text-align: left;"><span style="font-family: courier;"> byte[] imageBytes = memStream.ToArray();</span></div><div style="text-align: left;"><span style="font-family: courier;"> return Convert.ToBase64String(imageBytes);</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p style="text-align: left;">The above <i>SkiaUtils</i> class contains two static methods: <i>SaveImageToFile()</i> and <i>GetImageToBase64String()</i>. The method names are self-explanatory. Let us use these methods in our application. Add the following code to the bottom of <i>Program.cs</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// generate a random number between 0 and 200 to be used for filename</span></span></div><div style="text-align: left;"><span style="font-family: courier;">var random = new Random().Next(0, 200);</span></div></blockquote><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// use SkiaUtils class to save the image as a .png file</span></span></div><div style="text-align: left;"><span style="font-family: courier;">string filename = await SkiaUtils.SaveImageToFile(imageUrl, 1024, 1024, $"{random}-image.png");</span></div></blockquote><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// use SkiaUtils class to get base64 string representation of the image</span></span></div><div style="text-align: left;"><span style="font-family: courier;">var base64Image = await SkiaUtils.GetImageToBase64String(imageUrl, 1024, 1024);</span></div></blockquote><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// save base64 string representation of the image to a text file</span></span></div><div style="text-align: left;"><span style="font-family: courier;">File.WriteAllText($"{random}-base64image.txt", base64Image);</span></div></blockquote><h2 style="text-align: left;"><span style="font-family: courier;">R</span>unning App</h2><p>Let’s try it out. Run the app in a terminal window with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet run</span></p><p>The user is prompted with “<i>Enter a phrase to generate an image from:</i>”. I entered “<i>a camel roaming the streets of New York</i>”. This is the output I received:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhRF4s9bfCgusQB7v8Mjx00NhsCR32sOU2iDC_-lHKEahWZ8DO_OWRn-8i6UcZKp4mZJkNQ1ls0w89LfjQpurlNMZxUjRQ1RDUHQ9sxJxBSJdPIsMiqSEUhXM3YElxbzt_gnc_POxzTdVGxaIY3unWq0WjK50fTBJOT0wpna9j5T6bx5-W-oMWPXc8y2gi6" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="516" data-original-width="2408" height="138" src="https://blogger.googleusercontent.com/img/a/AVvXsEhRF4s9bfCgusQB7v8Mjx00NhsCR32sOU2iDC_-lHKEahWZ8DO_OWRn-8i6UcZKp4mZJkNQ1ls0w89LfjQpurlNMZxUjRQ1RDUHQ9sxJxBSJdPIsMiqSEUhXM3YElxbzt_gnc_POxzTdVGxaIY3unWq0WjK50fTBJOT0wpna9j5T6bx5-W-oMWPXc8y2gi6=w640-h138" width="640" /></a></div><br />I copied and pasted the URL into my browser. This is what the image looked like:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjVWfeEqM9zU6E3k9F9oz_i8mka79tgo_jkuIW9vMGxj9q4Rz6KISgKd_KnNyg8WDxXzC9L44dBn27dTt-osTjV8aX-55WWJ5BEQxsFuF7uQx2l_f5jJrOn2tZHathOPKLVAcwQ6zTXScLmFfl4NtwXpKdm1v3SeJT1iAevKC6tmDpuOxW4t39k-1t3NTsm" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="719" data-original-width="854" height="337" src="https://blogger.googleusercontent.com/img/a/AVvXsEjVWfeEqM9zU6E3k9F9oz_i8mka79tgo_jkuIW9vMGxj9q4Rz6KISgKd_KnNyg8WDxXzC9L44dBn27dTt-osTjV8aX-55WWJ5BEQxsFuF7uQx2l_f5jJrOn2tZHathOPKLVAcwQ6zTXScLmFfl4NtwXpKdm1v3SeJT1iAevKC6tmDpuOxW4t39k-1t3NTsm=w400-h337" width="400" /></a></div><p></p><p>Two files were created in the root folder of the console application - namely: 94-image.png and 94-base64image.txt. Note that your filenames could be different because the numbers in the name are randomly generated.</p><p>You can double-click on the <i>.png</i> image file to view it in the default image app on your computer.</p><h2 style="text-align: left;">Viewing Base64 representation of image in a web page</h2><div><div class="separator" style="clear: both; text-align: left;">In the root folder of your console application, create a file named <i>index.html </i>and add to it the following HTML/JavaScript code:</div><div class="separator" style="clear: both; text-align: left;"><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><!DOCTYPE html></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><html lang="en"></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><head></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <meta charset="UTF-8"></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <meta name="viewport" content="width=device-width, </span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> initial-scale=1.0"></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <title>Read Base64 image</title></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"></head></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><body></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <input type="file" id="fileInput" /></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <img src="" id="img"/></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <script></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> document.getElementById('fileInput')</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> .addEventListener('change', (event) => {</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> const file = event.target.files[0];</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> const reader = new FileReader();</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> reader.onload = function () {</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> const content = reader.result;</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> console.log(content);</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> document.getElementById('img')</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> .src = 'data:image/png;base64,' + content;</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> };</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> reader.onerror = function () {</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> console.error('Error reading the file');</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> };</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> reader.readAsText(file, 'utf-8');</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> });</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </script></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"></body></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"></html></span></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">The JavaScript in the above <i>index.html</i> file reads the text file and sets its Base64 content to the src attribute of an image tag.</div><h2 style="clear: both; text-align: left;">View Base64 representation of the image</h2><div class="separator" style="clear: both;">Double click on the index.html file on your file system.</div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgNWhLxqeKJoLQDldpYReHZ-8-7Zp9vS_9uQX8H4Ej72Ro5nc8rnh6X3nrSFzCbfTZI_LXa4Jw9Mgw9YEPeF8yj0gBks3141s6YO1uEhuZbPjfKznwuOLiC88RPFINfk8b9suRNA6yOOIPc5yoKG-7isSK0ClhuvMaD64o8cY-ZiIHmifw1_o3V0IJCom6R" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="68" data-original-width="402" height="54" src="https://blogger.googleusercontent.com/img/a/AVvXsEgNWhLxqeKJoLQDldpYReHZ-8-7Zp9vS_9uQX8H4Ej72Ro5nc8rnh6X3nrSFzCbfTZI_LXa4Jw9Mgw9YEPeF8yj0gBks3141s6YO1uEhuZbPjfKznwuOLiC88RPFINfk8b9suRNA6yOOIPc5yoKG-7isSK0ClhuvMaD64o8cY-ZiIHmifw1_o3V0IJCom6R" width="320" /></a></div>Navigate to the text file that contains the Base64 representation of the image and select it. You will see the same image that you had seen earlier loaded to the web page.</div><div class="separator" style="clear: both;"><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhc9a2KsvUs2YZIRHRXKdKpqNjKCRcNCErzC1eb2jSD2-ukf7F-Aj_YEMZM_Cp85AbrWyhhvsf_T1zoTk2JkJZMvxuAsL1UlqieZZUrqLqXQGE7I8VkCHjyOIPOjCe8hq7Tud1Y6tVOrjf9NAhdifYwuSQ8QyMivS-HF6_SwpoS_6NgyxEpJAZWcdZjQKTs" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="656" data-original-width="2050" height="204" src="https://blogger.googleusercontent.com/img/a/AVvXsEhc9a2KsvUs2YZIRHRXKdKpqNjKCRcNCErzC1eb2jSD2-ukf7F-Aj_YEMZM_Cp85AbrWyhhvsf_T1zoTk2JkJZMvxuAsL1UlqieZZUrqLqXQGE7I8VkCHjyOIPOjCe8hq7Tud1Y6tVOrjf9NAhdifYwuSQ8QyMivS-HF6_SwpoS_6NgyxEpJAZWcdZjQKTs=w640-h204" width="640" /></a></div><br /><h2 style="text-align: left;">Conclusion</h2></div></div></div><div>You can use the Image URL generated from the Dall-E 3 API, save it to your computer or generate a Base64 representation of the image,</div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-71101190105221171932024-01-24T13:42:00.000-08:002024-01-24T13:50:56.783-08:00Using C# code generated by Azure OpenAI Studio<p> In this article I will experience how one can use generated C# code in "Azure OpenAI Studio" to create your own application. Of course, it is assumed that you have an Azure subscription and access to Azure OpenAI.</p><h2 style="text-align: left;">Getting Started</h2><p>Go to <a href="https://oai.azure.com">https://oai.azure.com</a>.</p><p>Let us play in the “Chat playground”. Click on “Chat playground”.</p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjsHb6lASJOPj_U0UjUz8oNTa5KOyslGgNf-UsorXDoiiu0qGOMIl4-uUa88Sv682-4herOoduaYeXH1zjuBv_uVLuqAZn66JdMsI5P3s7Hb5uuzXHTlvYtb1QiEKqaYtL7CYnd4rXn7zMo8zr18bTW6QxhuQ6cUrZN9kz7TEKcgGcUo4vqcILMaiCMk1QN" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="327" data-original-width="945" height="222" src="https://blogger.googleusercontent.com/img/a/AVvXsEjsHb6lASJOPj_U0UjUz8oNTa5KOyslGgNf-UsorXDoiiu0qGOMIl4-uUa88Sv682-4herOoduaYeXH1zjuBv_uVLuqAZn66JdMsI5P3s7Hb5uuzXHTlvYtb1QiEKqaYtL7CYnd4rXn7zMo8zr18bTW6QxhuQ6cUrZN9kz7TEKcgGcUo4vqcILMaiCMk1QN=w640-h222" width="640" /></a></div><br /><div>We will start by grounding our AI bot. We can do that by telling the AI that it specializes in baking recipes. Enter the following to the "System Message":</div><p style="text-align: center;"><span style="font-family: courier;">You are an AI assistant that helps people with baking.</span></p><div>Next, add a prompt requesting a recipe:</div><p style="text-align: center;"><span style="font-family: courier;">Give me an easy recipe for baking a loaf of bread that takes about 30 minutes preparation and about 40 minutes in the oven.</span></p><div>The AI will suggest to you a recipe. This is the response I received:</div>
<div style="margin-left: 30px;">
<p style="text-align: left;"><span style="font-family: courier;">Certainly! Here's a simple recipe for baking a loaf of bread with a total time of approximately 1 hour and 10 minutes:</span></p><div><span style="font-family: courier;">Ingredients:</span></div><div><span style="font-family: courier;">•<span style="white-space: pre;"> </span>3 cups all-purpose flour</span></div><div><span style="font-family: courier;">•<span style="white-space: pre;"> </span>2 teaspoons active dry yeast</span></div><div><span style="font-family: courier;">•<span style="white-space: pre;"> </span>1 teaspoon salt</span></div><div><span style="font-family: courier;">•<span style="white-space: pre;"> </span>1 tablespoon sugar</span></div><div><span style="font-family: courier;">•<span style="white-space: pre;"> </span>1 cup warm water (around 110°F/43°C)</span></div><div><span style="font-family: courier;">•<span style="white-space: pre;"> </span>2 tablespoons olive oil</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">Instructions:</span></div><div><span style="font-family: courier;">In a large mixing bowl, combine the warm water and sugar. Stir until the sugar is dissolved, then sprinkle the yeast over the water. Let it sit for about 5 minutes until the yeast becomes frothy.</span></div><p style="text-align: left;"><span style="font-family: courier;">Add the salt and olive oil to the yeast mixture, and gradually add the flour, one cup at a time. Stir well after each addition until a shaggy dough forms.</span></p><p style="text-align: left;"><span style="font-family: courier;">Once the dough starts to come together, transfer it onto a lightly floured surface and knead for about 5-7 minutes until it becomes smooth and elastic. If the dough is too sticky, you can add a little more flour as needed.</span></p><div><span style="font-family: courier;">Shape the dough into a ball, and place it in a greased bowl. Cover the bowl with a clean kitchen towel or plastic wrap, and let the dough rise in a warm place for approximately 30 minutes or until it doubles in size.</span></div><p style="text-align: left;"><span style="font-family: courier;">Preheat your oven to 375°F (190°C) while the dough is rising.</span></p><p style="text-align: left;"><span style="font-family: courier;">After the dough has risen, punch it down gently to release any air bubbles. Transfer it to a greased loaf pan, and shape it into a loaf by tucking the edges under.</span></p><p style="text-align: left;"><span style="font-family: courier;">Cover the loaf pan again with the kitchen towel or plastic wrap, and let it rise for an additional 10 minutes.</span></p><p style="text-align: left;"><span style="font-family: courier;">Once the dough has risen for the second time, remove the cover and place the pan in the preheated oven. Bake for about 35-40 minutes or until the bread is golden brown and sounds hollow when tapped on the bottom.</span></p><p style="text-align: left;"><span style="font-family: courier;">Once baked, remove the bread from the oven and let it cool in the pan for a few minutes. Then transfer it to a wire rack to cool completely before slicing.</span></p><p style="text-align: left;"><span style="font-family: courier;">Enjoy your freshly baked bread!</span></p></div>
</div>
<div><h2 style="text-align: left;">Using generated C# code</h2><p style="text-align: left;">Click on “View code”.</p></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjjo_JGFGmilbX7aYqbK9C-o4_SU-6Xa7IjCmQr6j8x7Th1zgNCg4s7jVAN9QTIl_9nxUC1g_viNoy_eDb749emunjL3YECNOR2kzp3lDUsRXjiJM7ZHZawHjHEuIrEToZ5pvzS56MaQpZgPtPHkfIy3M5QWg_Kb4V4kvncsqQ0v0Lulh6QPfJh32k3rh9W" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="245" data-original-width="823" height="190" src="https://blogger.googleusercontent.com/img/a/AVvXsEjjo_JGFGmilbX7aYqbK9C-o4_SU-6Xa7IjCmQr6j8x7Th1zgNCg4s7jVAN9QTIl_9nxUC1g_viNoy_eDb749emunjL3YECNOR2kzp3lDUsRXjiJM7ZHZawHjHEuIrEToZ5pvzS56MaQpZgPtPHkfIy3M5QWg_Kb4V4kvncsqQ0v0Lulh6QPfJh32k3rh9W=w640-h190" width="640" /></a></div><br /><p style="text-align: left;"> Change the language to <i>C#</i>.</p></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgR8OCNo4nNRtEekFYYdx1K2uooMrWlTKeSNVYm6ueo2BisrKizQ87fRCAL53nSPgCVcYJcDOVDTRnpiJg-ZvyN9ift3Cc28WADR6jP-q1JHKPDruEJPQcpJdkO0uVxVdIAzxi_T-rw623ugrS9zog0bZlw_2aUfdvSb9Z2RUqfA_6CoCMlUyRhMj4BtEOo" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="350" data-original-width="977" height="230" src="https://blogger.googleusercontent.com/img/a/AVvXsEgR8OCNo4nNRtEekFYYdx1K2uooMrWlTKeSNVYm6ueo2BisrKizQ87fRCAL53nSPgCVcYJcDOVDTRnpiJg-ZvyN9ift3Cc28WADR6jP-q1JHKPDruEJPQcpJdkO0uVxVdIAzxi_T-rw623ugrS9zog0bZlw_2aUfdvSb9Z2RUqfA_6CoCMlUyRhMj4BtEOo=w640-h230" width="640" /></a></div></div><p style="text-align: left;">Note the message at the top. You are asked to add a specific version of a package in your C# app.</p><div class="separator" style="clear: both; text-align: center;"><p style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhgPt_-HXjva1yOk7hUx-0iqFf8yb3WcBbacGyiK9-CdWzgIzYoGPHS2CBquhasLDZocFZdIm5vKKLnqIYTqqeWWgNuOz0srBkfLBnA9bh9jdgxRWJhNc3PWC9JN3W9sKPMOJiEZoL4Cp8eC6igrxNOYwkcO3nqJqt_1a8iUn19nhmHQm4GaxrPof69-ld6" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="285" data-original-width="987" height="184" src="https://blogger.googleusercontent.com/img/a/AVvXsEhgPt_-HXjva1yOk7hUx-0iqFf8yb3WcBbacGyiK9-CdWzgIzYoGPHS2CBquhasLDZocFZdIm5vKKLnqIYTqqeWWgNuOz0srBkfLBnA9bh9jdgxRWJhNc3PWC9JN3W9sKPMOJiEZoL4Cp8eC6igrxNOYwkcO3nqJqt_1a8iUn19nhmHQm4GaxrPof69-ld6=w640-h184" width="640" /></a></p></div><div><span style="color: #274e13; font-family: courier;">// Note: The Azure OpenAI client library for .NET is in preview.</span></div><div><span style="color: #274e13; font-family: courier;">// Install the .NET library via NuGet: dotnet add package Azure.AI.OpenAI --version 1.0.0-beta.5</span></div></div><p style="text-align: left;">To get started with a basic console application, click on the “Learn more” link at the bottom.</p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg7hBhK5G-J0NdjwRf_lshVqVF7QHUewFozhKnpy3WfhOTsxiFLPNQlu-7qyve3ITDboj3vRXr_RFFlq-Y1N-79Qf4F7cnVC7CZuO5vxecz75U4o8ZKBld9YbWQgTMs6qx3eeJEcopXgqSr39n7eA1DezsHuBZw_8Xz5ouMH0YswHxG-XeynM-DBbplddVF" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="208" data-original-width="869" height="96" src="https://blogger.googleusercontent.com/img/a/AVvXsEg7hBhK5G-J0NdjwRf_lshVqVF7QHUewFozhKnpy3WfhOTsxiFLPNQlu-7qyve3ITDboj3vRXr_RFFlq-Y1N-79Qf4F7cnVC7CZuO5vxecz75U4o8ZKBld9YbWQgTMs6qx3eeJEcopXgqSr39n7eA1DezsHuBZw_8Xz5ouMH0YswHxG-XeynM-DBbplddVF=w400-h96" width="400" /></a></div><p style="text-align: left;">Choose C#.</p></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgm0TF0DL6bsCjoMahGvTzNoEXKBDKI0GeZtA2eWourz2BbUBFewjl00ULosxZ1oZFQ-Hpy5V98jbjU-RVwFZ-6ugGUxP1qyTVyvlEOuYw3WRdMp_ceIMqZcuYsA6IrkZ3avb6rKUdFG-KrTyaESm6BtWVxz9V1M32Jdoz-zjpbLvcH3Fk97g7M_TaUH93H" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="216" data-original-width="912" height="95" src="https://blogger.googleusercontent.com/img/a/AVvXsEgm0TF0DL6bsCjoMahGvTzNoEXKBDKI0GeZtA2eWourz2BbUBFewjl00ULosxZ1oZFQ-Hpy5V98jbjU-RVwFZ-6ugGUxP1qyTVyvlEOuYw3WRdMp_ceIMqZcuYsA6IrkZ3avb6rKUdFG-KrTyaESm6BtWVxz9V1M32Jdoz-zjpbLvcH3Fk97g7M_TaUH93H=w400-h95" width="400" /></a></div><br /><p style="text-align: left;">Under "Set up" you will be asked to create a new app and add a package to it.</p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">dotnet new console -n azure-openai-quickstart</span></div></div><div><div><span style="font-family: courier;">cd azure-openai-quickstart</span></div></div><div><div><span style="font-family: courier;">dotnet add package Azure.AI.OpenAI --prerelease</span></div></div></blockquote><div><p style="text-align: left;">Run the above commands, then replace the code in <i>Program.cs</i> with the code that was generated by “Azure AI Studio”.</p><p style="text-align: left;">You will need to enter the <i>AZURE_OPENAI_API_KEY</i> at around line 8 in <i>Program.cs</i>. This is given to you just below the sample code in “Azure OpenAI Studio”.</p></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgkLW6U5FFL5BAPeUt9JhAgQVFMdToWfh6MDvLTUa141F6cCpCK0o-mbLlAUSFrzP8ywjRX07yTIf6AB1GhAENJ4hXXCvDu00rivWcfUNJ46dUVdwoMbrSdC9hDLQLHzoSyUaYvHDijWth1iFqYUxDdtc_l7phmnFOh7Dzv-o6XHK0QwS26BGi20weE14Mb" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="321" data-original-width="1005" height="127" src="https://blogger.googleusercontent.com/img/a/AVvXsEgkLW6U5FFL5BAPeUt9JhAgQVFMdToWfh6MDvLTUa141F6cCpCK0o-mbLlAUSFrzP8ywjRX07yTIf6AB1GhAENJ4hXXCvDu00rivWcfUNJ46dUVdwoMbrSdC9hDLQLHzoSyUaYvHDijWth1iFqYUxDdtc_l7phmnFOh7Dzv-o6XHK0QwS26BGi20weE14Mb=w400-h127" width="400" /></a></div><p style="text-align: left;">Copy and paste the key into your code. This is what my code looked like after pasting the key:</p></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhLpnQuqdn2_Z32zWNi-V5FOky4KyLCCC5fgT6Csk9xLM_aJwaBj7YD-LHjj5mDOP9Xq2b1V1fPiVzPSKB_sYVnJK-3Iw_Q81wfhU_TtbFmT4XUu_71ISDDk1Ekc8vssvLANXQdPs60GCwX7gHE2EP-mXBAZAchw_RT21MPu_p9fou95bHR57imPhKwXYbv" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="163" data-original-width="992" height="66" src="https://blogger.googleusercontent.com/img/a/AVvXsEhLpnQuqdn2_Z32zWNi-V5FOky4KyLCCC5fgT6Csk9xLM_aJwaBj7YD-LHjj5mDOP9Xq2b1V1fPiVzPSKB_sYVnJK-3Iw_Q81wfhU_TtbFmT4XUu_71ISDDk1Ekc8vssvLANXQdPs60GCwX7gHE2EP-mXBAZAchw_RT21MPu_p9fou95bHR57imPhKwXYbv=w400-h66" width="400" /></a></div><p style="text-align: left;">If you run “dotnet build”, you will see some errors. This is because we did not use the specific preview version of the <i>Azure.AI.OpenAI</i> package that was suggested. Most likely you have a more recent version. The version I have at the time of writing (January 2024) is <i>1.0.0-beta.12</i>. All the errors pertain to the <i>ChatMessage </i>property when creating <i>ChatCompletionsOptions</i>. Replace the code for <i>responseWithoutStream </i>with the following:</p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">Response<ChatCompletions> responseWithoutStream = await client.GetChatCompletionsAsync(</span></div></div><div><div><span style="font-family: courier;"> new ChatCompletionsOptions() {</span></div></div><div><div><span style="font-family: courier;"> DeploymentName="gpt-35-turbo",</span></div></div><div><div><span style="font-family: courier;"> Messages =</span></div></div><div><div><span style="font-family: courier;"> {</span></div></div><div><div><span style="font-family: courier;"> new ChatRequestSystemMessage(@"You are an AI assistant that helps people find information."),</span></div></div><div><div><span style="font-family: courier;"> new ChatRequestUserMessage(@"Give me an easy recipe for baking a loaf of bread that takes about 30 minutes preparation and about 40 minutes in the oven."), </span></div></div><div><div><span style="font-family: courier;"> },</span></div></div><div><div><span style="font-family: courier;"> Temperature = (float)0.7,</span></div></div><div><div><span style="font-family: courier;"> MaxTokens = 800,</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> NucleusSamplingFactor = (float)0.95,</span></div></div><div><div><span style="font-family: courier;"> FrequencyPenalty = 0,</span></div></div><div><div><span style="font-family: courier;"> PresencePenalty = 0,</span></div></div><div><div><span style="font-family: courier;"> });</span></div></div></blockquote><div><p style="text-align: left;">Since nothing is output, let us display the AI response. Add the following code to the bottom of <i>Program.cs</i>:</p><p style="text-align: center;"><span style="font-family: courier;">Console.WriteLine(response.Choices[0].Message.Content);</span></p><p style="text-align: left;">Run the app and you will see the response from the AI. In my case I received a very similar response to what I previously got.</p><h2 style="text-align: left;">Conclusion</h2><p style="text-align: left;">"Azure OpenAI Studio" can help you get started with the development of a C# app that utilizes services from "Azure OpenAI".</p><p style="text-align: left;"><br /></p></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-32574861669339328252024-01-15T12:22:00.000-08:002024-01-15T12:30:38.916-08:00PHP meets OpenAI with image generation<p>Let's generate images using OpenAI's <i>dall-e-3</i> service. When using PHP, the open-source <i>openai-php/client </i>library facilitates the process. Check it out at <a href="https://github.com/openai-php/client">https://github.com/openai-php/client</a>.</p><p>In this article, we will learn how easy it is to generate an image with OpenAI and PHP. </p><p>Source code: <a href="https://github.com/medhatelmasry/openai-dalle3-php">https://github.com/medhatelmasry/openai-dalle3-php</a></p><h2 style="text-align: left;">Prerequisites</h2><p>In order to proceed, you will need the following:</p><p></p><ol style="text-align: left;"><li>Subscription with OpenAI - If you do not have a subscription, go ahead and register at <a href="https://openai.com/">https://openai.com/</a>.</li><li>PHP - You need to have PHP version 8.2 (or higher) installed on your computer. You can download the latest version from <a href="https://www.php.net/downloads.php">https://www.php.net/downloads.php</a>.</li><li>Composer – If you do not have Composer yet, download and install it for your operating system from <a href="https://getcomposer.org/download/">https://getcomposer.org/download/</a>.</li></ol><h2 style="text-align: left;">Getting Started</h2><p style="text-align: left;">In a suitable working directory, create a folder named <i>openai-dalle3-php</i> with the following terminal window command:</p><p style="text-align: center;"><span style="font-family: courier;">mkdir openai-dalle3-php</span></p><p style="text-align: left;">Change into the newly created folder with:</p><p style="text-align: center;"><span style="font-family: courier;">cd openai-dalle3-php</span></p><p style="text-align: left;">Using Composer, install the <i>openai-php/client</i> package by running this command:</p><p style="text-align: center;"><span style="font-family: courier;">composer require openai-php/client</span></p><p style="text-align: left;">We will be reading our ApoenAPI key from the .env text file. We need to install this package in order to do that.</p><p style="text-align: center;"><span style="font-family: courier;">composer require vlucas/phpdotenv</span></p><h2 style="text-align: left;">Getting an API KEY from OpenAI</h2><p style="text-align: left;">With your OpenAI credentials, login into <a href="https://openai.com/">https://openai.com/</a>. Click on <i>API</i>.</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi3fruoGE7JUKOTCFtexIZ125x_lVyo3Y2ZrOr_DrnZRlS1v7rWw4FovCSGD_ZqXNoXpcUfRRdrpNip27uDnh-b4TadMDjKblo9wintTrm6x7lMlqcjYArZJXw5igQpa9THYff2bfUaFxebhRwfphECPdTpADrkYdQ_BEZNiduHL5Hr5Fe7imEC4kXOF-5p" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="524" data-original-width="588" height="357" src="https://blogger.googleusercontent.com/img/a/AVvXsEi3fruoGE7JUKOTCFtexIZ125x_lVyo3Y2ZrOr_DrnZRlS1v7rWw4FovCSGD_ZqXNoXpcUfRRdrpNip27uDnh-b4TadMDjKblo9wintTrm6x7lMlqcjYArZJXw5igQpa9THYff2bfUaFxebhRwfphECPdTpADrkYdQ_BEZNiduHL5Hr5Fe7imEC4kXOF-5p=w400-h357" width="400" /></a></div><p style="text-align: left;">In the left navigation, click on "API keys". </p><div><p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjg8rDx-_rY9nmbwjZK5gQ-AFglZini_EzOGyuGqpWKTwHw18Cbrw3CFrryPJ0L8KRK08r-qcWZ55ZTpYcYuHctz3WVrxcypXzGbK1co9HUWjZ2AmFHdXzTDe-o8n2arFNOLNDDDFWyv_xjHyTXiq593PNpFd3YkBhuRthulnsl-2EcPAtFmi3VhWAFCpDH" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="360" data-original-width="177" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEjg8rDx-_rY9nmbwjZK5gQ-AFglZini_EzOGyuGqpWKTwHw18Cbrw3CFrryPJ0L8KRK08r-qcWZ55ZTpYcYuHctz3WVrxcypXzGbK1co9HUWjZ2AmFHdXzTDe-o8n2arFNOLNDDDFWyv_xjHyTXiq593PNpFd3YkBhuRthulnsl-2EcPAtFmi3VhWAFCpDH=w197-h400" width="197" /></a></div><br />Click on the "+ Create new secret key" button.<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEijpu71TAM36nxRXDt8Sz98wiowMXWjuruzm69unWSv3_E1m6QhAh7VQbm0MuKuE29TzvjZPkNWQEB2QrGmS8MTuT0wTaOl9xB-kIvQEi308uZmQ9hmtobHw_ovl2DEusk_haHLa_DNuKhg4BL-6xgBF_kFf57JT5Do797Z4I98IXJx0VAp2zUBu-76zc9f" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="52" data-original-width="201" height="83" src="https://blogger.googleusercontent.com/img/a/AVvXsEijpu71TAM36nxRXDt8Sz98wiowMXWjuruzm69unWSv3_E1m6QhAh7VQbm0MuKuE29TzvjZPkNWQEB2QrGmS8MTuT0wTaOl9xB-kIvQEi308uZmQ9hmtobHw_ovl2DEusk_haHLa_DNuKhg4BL-6xgBF_kFf57JT5Do797Z4I98IXJx0VAp2zUBu-76zc9f" width="320" /></a></div><br />Give the new key a name. I named it 20240115 representing the date when it was created. You may wish to give it a more meaningful or creative name. Once you are happy with the name, click on the "Create secret key" to generate the key.<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiFPcQKCv0iAjwfQdF03xFr5khQUjlUjNrfTh6TTaFmJkfKCyyTCnF2l9zg4SbFWaZmhHTVOYP8D25yUrqT6sPB8f-cTWUXWz9GD0_QidIKP_LpRuV3gzonpgsvYQ5j9zIzm0GtZiAACSMO3QiitfRFaCH6SCdcyp-TWYS1LB4bcjHWkX0w0IvNIkWeYesU" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="193" data-original-width="502" height="154" src="https://blogger.googleusercontent.com/img/a/AVvXsEiFPcQKCv0iAjwfQdF03xFr5khQUjlUjNrfTh6TTaFmJkfKCyyTCnF2l9zg4SbFWaZmhHTVOYP8D25yUrqT6sPB8f-cTWUXWz9GD0_QidIKP_LpRuV3gzonpgsvYQ5j9zIzm0GtZiAACSMO3QiitfRFaCH6SCdcyp-TWYS1LB4bcjHWkX0w0IvNIkWeYesU=w400-h154" width="400" /></a></div><br />Click on the <i>copy</i> button beside the key and paste the API-Key somewhere safe as we will need to use it later on. Note that you cannot view this key again. Click on <i>Done</i> to dismiss the dialog.<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgWo2Sro165EDqHiKlRNFGVcRbRxINpIFY4u6_Ub3tfkAoU_hFHaQu6RMQVEmVmvFHRXXEaWAQHzkaaKW8O-zvqwl-oJSclZtibdkEoCRdwVt-j9AitU-gcvQlXYSHp0YiwFlhPGobQrNmVyl57-_Hf0A8tVr_YG8JwtICztxVj7H38YuK6L4A25gaw44Dt" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="251" data-original-width="500" height="201" src="https://blogger.googleusercontent.com/img/a/AVvXsEgWo2Sro165EDqHiKlRNFGVcRbRxINpIFY4u6_Ub3tfkAoU_hFHaQu6RMQVEmVmvFHRXXEaWAQHzkaaKW8O-zvqwl-oJSclZtibdkEoCRdwVt-j9AitU-gcvQlXYSHp0YiwFlhPGobQrNmVyl57-_Hf0A8tVr_YG8JwtICztxVj7H38YuK6L4A25gaw44Dt=w400-h201" width="400" /></a></div></div><p style="text-align: left;">We will create a text file named <i>.env</i> in our <i>openai-dalle3-php</i> folder with the following content:</p><p style="text-align: center;"><span style="font-family: courier;">OPENAI_API_KEY=put-your-openai-api-key-here</span></p><p style="text-align: left;">Set the API-Key as the value of OPENAI_API_KEY. This may look like this:</p><p style="text-align: center;"><span style="font-family: courier;">OPENAI_API_KEY=sk-OOghjTs8GsuHQklTCWOeT3BasdGJAjklBr3tr8ViZKv21BRN</span></p><h2 style="text-align: left;">Let's get coding</h2><p style="text-align: left;">We can generate images by obtaining a URL to the newly created image, or by getting the Base-64 representation of the image. We will try both ways.</p><div><p></p><h4 style="text-align: left;">1) Get URL of the image</h4><p style="text-align: left;">In the <i>openai-dalle3-php</i> folder, create a file named <i>image-url.php</i> with the following content:</p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div style="text-align: left;"><span style="font-family: courier;"><?php</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">require_once __DIR__ . '/vendor/autoload.php';</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">$dotenv->load();</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"> </span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">$client = OpenAI::client($_ENV['OPENAI_API_KEY']);</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">$response = $client->images()->create([</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"> 'model' => 'dall-e-3',</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"> 'prompt' => 'A panda flying over Paris at night',</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"> 'n' => 1,</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"> 'size' => '1024x1024',</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"> 'response_format' => 'url',</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">]);</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">foreach ($response->data as $data) {</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"> $data->url; </span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"> $data->b64_json; <span style="color: #38761d;">// null</span></span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// display the image</span></span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">echo '<img src="' . $data->url . '" />';</span></div></div><div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div></div><div><div style="text-align: left;"><span style="font-family: courier;">?></span></div></div></blockquote><div><p style="text-align: left;">In the above code, note the following:</p><p style="text-align: left;"></p><ul style="text-align: left;"><li>we read in the API Key from <i>.env</i> file and pass it in the <i>OpenAI::client($_ENV['OPENAI_API_KEY']);</i> statement</li><li>we request a URL response with an image size 1024 x 1024</li><li>we prompt the dall-e-3 service to generate an image of 'A panda flying over Paris at night'.</li></ul><p></p><p style="text-align: left;">To run the app, start the PHP web server to listen on port number 8888 with the following command in the <i>openai-dalle3-php</i> folder.</p><p style="text-align: center;"><span style="font-family: courier;">php -S localhost:8888</span></p><p style="text-align: left;">You can view the resulting image that gets created by OpenAI by pointing your browser to the following URL:</p><p style="text-align: center;"><span style="font-family: courier;"><a href="http://localhost:8888/image-url.php">http://localhost:8888/image-url.php</a></span></p><p style="text-align: left;">This is the image that got created for me:</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjMV0hiFrQg2scaTXQuvqSijZ6l3XJrYDQ3j6IM1aLSe0LCHeyFhg-OQ1T44d8YwKVLmnvEHD07veu1J20r-tlqN0f5A74beH5cdKRLrLA6NoqG4py7AszuSYDOmQHWYE2F5jI-5mC02KY2sz-qisoY3_d1ONmXKVCtegiUVWPQFR3PIE1UhZDobL9rfoz8" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="519" data-original-width="520" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEjMV0hiFrQg2scaTXQuvqSijZ6l3XJrYDQ3j6IM1aLSe0LCHeyFhg-OQ1T44d8YwKVLmnvEHD07veu1J20r-tlqN0f5A74beH5cdKRLrLA6NoqG4py7AszuSYDOmQHWYE2F5jI-5mC02KY2sz-qisoY3_d1ONmXKVCtegiUVWPQFR3PIE1UhZDobL9rfoz8=w400-h400" width="400" /></a></div><br />Every time you run the app you will likely get a different image.<p></p><h4>2) Get Base64 encoding of the image</h4><p style="text-align: left;">Create another file named <i>image-b64.php</i> with the following content:</p><p style="text-align: left;"><span style="font-family: courier;"><span> </span><?php</span></p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div style="text-align: left;"><div><span style="font-family: courier;">require_once __DIR__ . '/vendor/autoload.php';</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"><br /></span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;">$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;">$dotenv->load();</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"> </span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;">$client = OpenAI::client($_ENV['OPENAI_API_KEY']);</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"><br /></span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;">$response = $client->images()->create([</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"> 'model' => 'dall-e-3',</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"> 'prompt' => 'A panda flying over Paris at night',</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"> 'n' => 1,</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"> 'size' => '1024x1024',</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"> 'response_format' => 'b64_json',</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;">]);</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"><br /></span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;">foreach ($response->data as $data) {</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"> $data->url; <span style="color: #38761d;">// null</span></span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"> $data->b64_json; </span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;">}</span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"><br /></span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"><div><span style="color: #38761d;">// display base64 encoded image</span></div></span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"><div>echo '<img src="data:image/jpeg;base64,' . $data->b64_json. '" />';</div></span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;"><div><br /></div></span></div></div></div><div><div style="text-align: left;"><div><span style="font-family: courier;">?></span></div></div></div></blockquote><div><p style="text-align: left;"><span style="font-family: inherit;">The only changes that were made are in the following lines of code:</span></p><p style="text-align: left;"><span style="font-family: inherit;">(1) </span><span style="font-family: courier; text-align: center;">'response_format' => 'b64_json',</span></p><p style="text-align: left;">Whereas previously, we requested a URL, this time we request base-64 encoding.</p><p style="text-align: left;">(2) <span style="font-family: courier; text-align: center;">echo '<img src="data:image/jpeg;base64,' . $data->b64_json. '" />';</span></p><p style="text-align: left;">When rendering the image, we use base64 rendering instead of an image URL.</p><p style="text-align: left;">Point your browser to <a href="http://localhost:8888/image-b64.php">http://localhost:8888/image-b64.php</a>. This is what I experienced:</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjdSGM4qkZBcyOyIqQV7EoSHwWT6sZASuxi0NIOnAp9MwE8GupBVynE_eKorWnwi28LlPTv_X7cOoja3c9AShGQztBYEDGvUajcYYMcXdqROGgeAepxENUIoJ4UuETun4hOtc5-pCmJPzA7sS8L1Udl4jHhWMub7xLjwI9xksEGtbt3HMs_TXLhkwm-fb8k" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="518" data-original-width="524" height="395" src="https://blogger.googleusercontent.com/img/a/AVvXsEjdSGM4qkZBcyOyIqQV7EoSHwWT6sZASuxi0NIOnAp9MwE8GupBVynE_eKorWnwi28LlPTv_X7cOoja3c9AShGQztBYEDGvUajcYYMcXdqROGgeAepxENUIoJ4UuETun4hOtc5-pCmJPzA7sS8L1Udl4jHhWMub7xLjwI9xksEGtbt3HMs_TXLhkwm-fb8k=w400-h395" width="400" /></a></div><h2 style="text-align: left;">Conclusion</h2><p></p><p style="text-align: left;">There are may services that you can consume from OpenAP, like chat completion, text completion, embeddings, etc.. Now that you know how things work, go ahead and try some of the other services.</p><p></p></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-19612040169079093862024-01-13T14:25:00.000-08:002024-02-07T14:27:10.214-08:00Generate Images with Azure OpenAI Dall-E 3, Semantic Kernel, and C#<p>It is very easy to generate images using the OpenAI Dall-E 3 service and Semantic Kernel. You provide the text describing what you want and OpenAI will generate for you the image. In this tutorial, we will use Semantic Kernel and Azure OpenAI to do exactly that.</p><p>Source Code: <a href="https://github.com/medhatelmasry/DalleImage.git">https://github.com/medhatelmasry/DalleImage.git</a></p><p>Companion Video: <a href="https://youtu.be/Dr727OhX4HU">https://youtu.be/Dr727OhX4HU</a></p><h2><b>What is Semantic Kernel?</b></h2><p>This is the official definition obtained from <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Create AI agents with Semantic Kernel | Microsoft Learn</a>:</p><p><i>Semantic Kernel is an open-source SDK that lets you easily build agents that can call your existing code. As a highly extensible SDK, you can use Semantic Kernel with models from OpenAI, Azure OpenAI, Hugging Face, and more!</i> </p><h2 style="text-align: left;">Getting Started</h2><p>In a suitable directory, create a console application named <i>DalleImage</i> and add to it two packages needed for our application with the following terminal window commands:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">dotnet new console -o DalleImage</span></div><div style="text-align: left;"><span style="font-family: courier;">cd DalleImage</span></div><div style="text-align: left;"><span style="font-family: courier;">dotnet add package Microsoft.SemanticKernel</span></div><div style="text-align: left;"><span style="font-family: courier;">dotnet add package System.Configuration.ConfigurationManager</span></div></blockquote><p>Create a file named <i>App.config</i> in the root folder of the console application and add to it the important parameters that allow access to the Azure OpenAI service. Contents of <i>App.config</i> are like the following:</p><div style="text-align: left;"><<span style="font-family: courier;">?xml version="1.0"?><br /><configuration><br /> <appSettings><br /> <add key="endpoint" value="https://fake.openai.azure.com/" /><br /> <add key="api-key" value="fakekey-fakekey-fakekey-fakekey" /><br /> <add key="gpt-deployment" value="gpt-35-turbo" /><br /> <add key="dalle-deployment" value="dall-e-3" /><br /> </appSettings><br /></configuration></span></div><p>NOTE: Since I cannot share the <i>endpoint</i> and <i>apiKey</i> with you, I have fake values for these settings.</p><p>Currently, the Dall-E 3 model is in preview and only available in the "Sweden Central" Azure data centre according to <a href="https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#dall-e-models-preview">https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models#dall-e-models-preview</a>. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgh6r23RC7avzdDjb7Hk89jKldPtAMt8G6KRNNoIUGc38wik6dTq70YBzA66rTPa1t16mWXjosC6jXtOEZDzZZI3CCegKfdQ1DCg7TfyaEeem4uVWQc1ds4UnNzaX5m9KAMRN5XQwfvawWAuaZ_AZOgpISihcuV81juQUrHEg4Q-AI8rYGQ9Js38kleOQmJ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="237" data-original-width="617" height="154" src="https://blogger.googleusercontent.com/img/a/AVvXsEgh6r23RC7avzdDjb7Hk89jKldPtAMt8G6KRNNoIUGc38wik6dTq70YBzA66rTPa1t16mWXjosC6jXtOEZDzZZI3CCegKfdQ1DCg7TfyaEeem4uVWQc1ds4UnNzaX5m9KAMRN5XQwfvawWAuaZ_AZOgpISihcuV81juQUrHEg4Q-AI8rYGQ9Js38kleOQmJ=w400-h154" width="400" /></a></div><p></p><h2 style="text-align: left;">Let's Code</h2><p>Open <i>Program.cs</i> and delete all its contents. Add the following <i>using</i> statements at the top:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">using System.Configuration;</span></div><div style="text-align: left;"><span style="font-family: courier;">using Microsoft.SemanticKernel;</span></div><div style="text-align: left;"><span style="font-family: courier;">using Microsoft.SemanticKernel.Connectors.OpenAI;</span></div><div style="text-align: left;"><span style="font-family: courier;">using Microsoft.SemanticKernel.TextToImage;</span></div></blockquote><p>We need to read the <i>App.config</i> file settings into our application. We will use the <i>ConfigurationManager</i> from namespace <i>System.Configuration</i>. To read settings from <i>App.config</i> with <i>ConfigurationManager</i>, append the following code to <i>Program.cs</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #274e13; font-family: courier;">// Get configuration settings from App.config</span></div><div style="text-align: left;"><span style="font-family: courier;">string _endpoint = ConfigurationManager.AppSettings["endpoint"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;">string _apiKey = ConfigurationManager.AppSettings["api-key"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;">string _dalleDeployment = ConfigurationManager.AppSettings["dalle-deployment"]!;</span></div><div style="text-align: left;"><span style="font-family: courier;">string _gptDeployment = ConfigurationManager.AppSettings["gpt-deployment"]!;</span></div></blockquote><p>Currently, we need to disable certain warning directives by adding the following into the <i>.csproj </i>file inside the <i><PropertyGroup></i> block:</p><p style="text-align: center;"><span style="font-family: courier;"><NoWarn>SKEXP0001, SKEXP0002, SKEXP0011, SKEXP0012</NoWarn></span></p><p>Then, append this code to <i>Program.cs</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// Create a kernel builder</span></div><div style="text-align: left;"><span style="font-family: courier;">var builder = Kernel.CreateBuilder();</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// Add OpenAI services to the kernel</span></div><div style="text-align: left;"><span style="font-family: courier;">builder.AddAzureOpenAITextToImage(_dalleDeployment, _endpoint, _apiKey);</span></div><div style="text-align: left;"><span style="font-family: courier;">builder.AddAzureOpenAIChatCompletion(_gptDeployment, _endpoint, _apiKey);</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// Build the kernel</span></div><div style="text-align: left;"><span style="font-family: courier;">var kernel = builder.Build();</span></div></blockquote><span style="font-family: courier;"><div><span style="font-family: courier;"><br /></span></div>W</span>e created a <i>builder</i> object from <i>SematicKernel</i>, added the <i>AddAzureOpenAITextToImage</i> and <i>AddAzureOpenAIChatCompletion</i> services, then obtained an instance of the <i>kernel</i> object.<br /><p>Get an instance of the "Dall-E" service from the <i>kernel</i> with the following code:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// Get AI service instance used to generate images</span></div><div style="text-align: left;"><span style="font-family: courier;">var dallE = kernel.GetRequiredService<ITextToImageService>();</span></div></blockquote><p>Let us create a prompt that generates an image representing a phrase entered by the user. Append this code to <i>Program.cs</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// create execution settings for the prompt</span></div><div style="text-align: left;"><span style="font-family: courier;">var prompt = @"</span></div><div style="text-align: left;"><span style="font-family: courier;">Think about an artificial object that represents {{$input}}.";</span></div></blockquote><p>We then configure the prompt execution settings with:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">var executionSettings = new OpenAIPromptExecutionSettings {</span></div><div style="text-align: left;"><span style="font-family: courier;"> MaxTokens = 256,</span></div><div style="text-align: left;"><span style="font-family: courier;"> Temperature = 1</span></div><div style="text-align: left;"><span style="font-family: courier;">};</span></div></blockquote><p><i>Temperature</i> is a measure of how creative you want the AI to be. This ranges from 0 to 1, where 0 is least creative and 1 is most creative.</p><p>We will create a semantic function from our prompt with:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// create a semantic function from the prompt</span></div><div style="text-align: left;"><span style="font-family: courier;">var genImgFunction = kernel.CreateFunctionFromPrompt(prompt, executionSettings);</span></div></blockquote><p>Let us ask the user for input with:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// Get a phrase from the user</span></div><div style="text-align: left;"><span style="font-family: courier;">Console.WriteLine("Enter a phrase to generate an image from: ");</span></div><div style="text-align: left;"><span style="font-family: courier;">string? phrase = Console.ReadLine();</span></div><div style="text-align: left;"><span style="font-family: courier;">if (string.IsNullOrEmpty(phrase)) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> Console.WriteLine("No phrase entered.");</span></div><div style="text-align: left;"><span style="font-family: courier;"> return;</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p style="text-align: left;">Next, ask the <i>kernel</i> to combine the <i>prompt</i> with the <i>input</i> received from to user, producing a <i>description</i>.</p><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// Invoke the semantic function to generate an image description</span><br />var imageDescResult = await kernel.InvokeAsync(genImgFunction, new() { ["input"] = phrase });<br />var imageDesc = imageDescResult.ToString();</span></div><p>Finally, ask Dall-E service to do the important work of generating an image based on the description. It returns an image url. This is done with the following code:</p><div style="text-align: left;"><span style="color: #38761d; font-family: courier;">// Use DALL-E 3 to generate an image. <br /></span><span style="font-family: courier;"><span style="color: #38761d;">// In this case, OpenAI returns a URL (though you can ask to return a base64 image)</span><br /></span><span style="font-family: courier;">var imageUrl = await dallE.GenerateImageAsync(imageDesc.Trim(), 1024, 1024);</span></div><p>Let’s print the output URL so that the user can pop it into a browser to see what it looks like:</p><p style="text-align: center;"><span style="font-family: courier;">Console.WriteLine($"Image URL:\n\n{imageUrl}");</span></p><h2 style="text-align: left;">Running App</h2><p>Let’s try it out. Run the app in a terminal window with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet run</span></p><p>The user is prompted with “<i>Enter a phrase to generate an image from:</i>”. I entered “<i>a lobster flying over the pyramids in giza</i>”, and received this output:</p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjoFlFT7glfe6qOhWWsHz5kI2eLph8Xmw9a4vodG10etbJHdadufwwpFVEvm2Z9dtxBOnQw8eXYv2JLd7eZbjWMAk1GRdbQaHH2VaP7NOiHTjoIpOfaKrpgmdyUdSDqwoZ7QXiyv9ehJ_eo2dHWjFMDuWtIYADz9PgdGtAksP7hv85gdB4WJyhmKhntMK-L" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="866" data-original-width="860" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEjoFlFT7glfe6qOhWWsHz5kI2eLph8Xmw9a4vodG10etbJHdadufwwpFVEvm2Z9dtxBOnQw8eXYv2JLd7eZbjWMAk1GRdbQaHH2VaP7NOiHTjoIpOfaKrpgmdyUdSDqwoZ7QXiyv9ehJ_eo2dHWjFMDuWtIYADz9PgdGtAksP7hv85gdB4WJyhmKhntMK-L=w397-h400" width="397" /></a></div><br />I find it pretty fascinating how OpenAI can generate images based on text-based descriptions. I hope you do too.</div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-24895951863582763272024-01-10T15:58:00.000-08:002024-01-28T14:06:19.735-08:00Build simple C# completion app with Azure OpenAI and Semantic Kernel Tool<p>In this walkthrough, I will show you how easy it is to use the 'Semantic Kernel Tool' in Visual Studio Code to create a cake baking skill without a single line of code. We will then build a C# console application that uses the skill.</p><p>Source: <a href="https://github.com/medhatelmasry/sk-library">https://github.com/medhatelmasry/sk-library</a></p><p>Companion Video: <a href="https://youtu.be/eI5Pr58gFZg">https://youtu.be/eI5Pr58gFZg</a></p><h2><b>What is Semantic Kernel?</b></h2><p>This is the official definition obtained from <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Create AI agents with Semantic Kernel | Microsoft Learn</a>:</p><p><i>Semantic Kernel is an open-source SDK that lets you easily build agents that can call your existing code. As a highly extensible SDK, you can use Semantic Kernel with models from OpenAI, Azure OpenAI, Hugging Face, and more!</i> </p><p>We now have an extension for Visual Studio Code that makes it very easy to build AI apps that use the large language models (LLMs) available through <a href="https://openai.com/" target="_blank">OpenAI</a>. </p><p>In order to proceed with this tutorial, you will need the following prerequisites:</p><p></p><ol><li>.NET 8.0 Framework</li><li>Visual Studio Code</li><li>Access to Azure OpenAI</li><li>Install the 'Semantic Kernel Tool' extension into Visual Studio Code.</li></ol><div><p style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiMM459zEalZKsfgxyEtrWip1Q6WHGFpvsShtJFzvs52oK9jigEVsMfcuOTDP9wfs9cCyLL1ecS2ZnINL6YNcS_0CviXnM3DN8ZQMe4gkGN33ThetWJAaGIJNt3OZOxxOWa9-kvt-H2Z0iBBOcx2cb6K6r_373lEJNa5_CMBoGxzHLQIjF7wTVRsM0JQZOQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="468" data-original-width="1838" height="162" src="https://blogger.googleusercontent.com/img/a/AVvXsEiMM459zEalZKsfgxyEtrWip1Q6WHGFpvsShtJFzvs52oK9jigEVsMfcuOTDP9wfs9cCyLL1ecS2ZnINL6YNcS_0CviXnM3DN8ZQMe4gkGN33ThetWJAaGIJNt3OZOxxOWa9-kvt-H2Z0iBBOcx2cb6K6r_373lEJNa5_CMBoGxzHLQIjF7wTVRsM0JQZOQ=w640-h162" width="640" /></a></p><h2 style="clear: both; text-align: left;">Getting Started</h2><p style="clear: both; text-align: left;">In a suitable working directory, create a folder named <i>sk-library</i>, change directory into the new folder, then start Visual Studio Code with the following terminal commands:</p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div style="clear: both; text-align: left;"><span style="font-family: courier;">mkdir sk-library</span></div></div><div><div style="clear: both; text-align: left;"><span style="font-family: courier;">cd sk-library</span></div></div><div><div style="clear: both; text-align: left;"><span style="font-family: courier;">code .</span></div></div></blockquote><p style="text-align: left;">In Visual Studio Code, select: View >> Command Palette...</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi9iq8KXmreTBs8kbl-mPCrZ6O23tmQFGduU9QNwnr1KSbiZwMwGspjH94h_WvjThSoYvY9a07pl1d71dr6aTnPqsaoEOx5dLdkVpVfF_5HWkMexC0CEs-KRRQWTLHgyUqeh8TMqQYYDStHx5Ra7gPDFPelomiOkoCr4WltRCdGIXrgHJ-rbCE0WyqpkdVa" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="141" data-original-width="439" height="103" src="https://blogger.googleusercontent.com/img/a/AVvXsEi9iq8KXmreTBs8kbl-mPCrZ6O23tmQFGduU9QNwnr1KSbiZwMwGspjH94h_WvjThSoYvY9a07pl1d71dr6aTnPqsaoEOx5dLdkVpVfF_5HWkMexC0CEs-KRRQWTLHgyUqeh8TMqQYYDStHx5Ra7gPDFPelomiOkoCr4WltRCdGIXrgHJ-rbCE0WyqpkdVa" width="320" /></a></div><p style="text-align: left;">Select "Add AI Endpoint" from the list.</p></div><div class="separator" style="clear: both; text-align: left;"><p style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjRkEbbMqq-5585aCa_z9pxp2KI1d6lwaPxaYirqk5TKJbumAuSswetis9BclIm79JLnKYhH1MV0OQbCG8n-6A06OY_EJv9uf3VtRD7URyCBtD52qbge-60ZhbTlCljHxZslFRzA20cFQKhJ6PNfZzQ7KQQ_qZEZbqFqNKiIZPuXoIrIawieNBc_qi5ACMj" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="138" data-original-width="547" height="81" src="https://blogger.googleusercontent.com/img/a/AVvXsEjRkEbbMqq-5585aCa_z9pxp2KI1d6lwaPxaYirqk5TKJbumAuSswetis9BclIm79JLnKYhH1MV0OQbCG8n-6A06OY_EJv9uf3VtRD7URyCBtD52qbge-60ZhbTlCljHxZslFRzA20cFQKhJ6PNfZzQ7KQQ_qZEZbqFqNKiIZPuXoIrIawieNBc_qi5ACMj" width="320" /></a></p>You will get asked to choose between AzureOpenAI and OpenAI. I will choose AzureOpenAI in this example.</div><p style="clear: both; text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhCMDPrQWhl_xgtHJKgWdBkx-y94CFekutzu5A2sfjxaBO8AdnL4fAJ2WTyARsV4X-l4rtDZSQd_5esAuqA-XaAFEe9-sDEE0h1DkJA7GU1ELN00gCctpU3gcPGpdC9xgtF9SRGksHL9cod4jDxANvx7khHAohMoQxQqktjtr4Wy6qfb4lgO_PYMCW6Ns9a" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="176" data-original-width="373" height="151" src="https://blogger.googleusercontent.com/img/a/AVvXsEhCMDPrQWhl_xgtHJKgWdBkx-y94CFekutzu5A2sfjxaBO8AdnL4fAJ2WTyARsV4X-l4rtDZSQd_5esAuqA-XaAFEe9-sDEE0h1DkJA7GU1ELN00gCctpU3gcPGpdC9xgtF9SRGksHL9cod4jDxANvx7khHAohMoQxQqktjtr4Wy6qfb4lgO_PYMCW6Ns9a" width="320" /></a></div><br />The next ask is the name of the model that was created on the Azure portal in the AzureOpenAI service. A suitable model for this completion task is <i>text-davinci-003</i>.<p></p><p style="clear: both; text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg8o0LnAlW0a_VGNeH-Tfh11oxAoEIS_-FYlTEFCTiR-98DKmAI2TOAjITxgngDeFqXAfvMGMXJBVcqQF6ohDt730D97_FKaiqqXkAj6Ma9O_hHwzh0blEajbXO3CqLwkoVfdqLu4KhtYeXuCHa5OGvoAIOibhQwLQJba1LiEh8fZ0GYUeSuvwf6pXCAzuE" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="125" data-original-width="640" height="79" src="https://blogger.googleusercontent.com/img/a/AVvXsEg8o0LnAlW0a_VGNeH-Tfh11oxAoEIS_-FYlTEFCTiR-98DKmAI2TOAjITxgngDeFqXAfvMGMXJBVcqQF6ohDt730D97_FKaiqqXkAj6Ma9O_hHwzh0blEajbXO3CqLwkoVfdqLu4KhtYeXuCHa5OGvoAIOibhQwLQJba1LiEh8fZ0GYUeSuvwf6pXCAzuE=w400-h79" width="400" /></a></div>We will be asked to enter the Azure OpenAI Endpoint, which you can obtain from Azure.<p></p><p style="clear: both; text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg-3he4z8ftquHCm5ZxPsaOWwleEsalFxb-wu3v5EFfibEi5PfR1j7norUf8eEFBk__9bRnFUzSS1B54fn60pgb5ph6Dvlncr598jBms1tHnJjdsvfYbN6aseJwpV9nPWtX6a82sopxAm0CSPCLYCZaCWxRRwm6cB4Lhu6WrESU6umIJJyF41GmbrC-ILzQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="142" data-original-width="644" height="89" src="https://blogger.googleusercontent.com/img/a/AVvXsEg-3he4z8ftquHCm5ZxPsaOWwleEsalFxb-wu3v5EFfibEi5PfR1j7norUf8eEFBk__9bRnFUzSS1B54fn60pgb5ph6Dvlncr598jBms1tHnJjdsvfYbN6aseJwpV9nPWtX6a82sopxAm0CSPCLYCZaCWxRRwm6cB4Lhu6WrESU6umIJJyF41GmbrC-ILzQ=w400-h89" width="400" /></a></div>Finally, we must enter the Azure OpenAI Key.<p></p><p style="clear: both; text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhUkoKbSGWQyLtghKsJIfHb6qmomccrMV4O7zcP4pEHSiaXxT5Mgp9fZzdgjFKpNi1rPSRvyL63SDsJCIVtCZbISGZWiEWdu8jKwZmR6TaLpniAJm_-wiC_Lxlrz_lkUgJbYXwYSJc6ATjgLdGqWRshnOSBX7YDaJEOsheyYqPj6VHxxyAFNtC63GFgvKQr" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="139" data-original-width="643" height="86" src="https://blogger.googleusercontent.com/img/a/AVvXsEhUkoKbSGWQyLtghKsJIfHb6qmomccrMV4O7zcP4pEHSiaXxT5Mgp9fZzdgjFKpNi1rPSRvyL63SDsJCIVtCZbISGZWiEWdu8jKwZmR6TaLpniAJm_-wiC_Lxlrz_lkUgJbYXwYSJc6ATjgLdGqWRshnOSBX7YDaJEOsheyYqPj6VHxxyAFNtC63GFgvKQr=w400-h86" width="400" /></a></div><br />If all goes well, you will receive this comforting message.<p></p><p style="clear: both; text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjef4FVysRGnAOAiTVXyvJ3kQJQjbeXBiGhqst2RxQ4iKX6iqr-HWmVE4FMUNEqJivHlW8PYG-lG_oSgEIKAR59MNOJSKNpima9U4ZMkNzWdhuMk9SxVBXrsWzk4K-0-zeebu2i1U0CSfx-9E0hD3dDtH7_ct96GXZl-tLOdV0oUWr3v30-9RJPORRK-oSX" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="62" data-original-width="646" height="39" src="https://blogger.googleusercontent.com/img/a/AVvXsEjef4FVysRGnAOAiTVXyvJ3kQJQjbeXBiGhqst2RxQ4iKX6iqr-HWmVE4FMUNEqJivHlW8PYG-lG_oSgEIKAR59MNOJSKNpima9U4ZMkNzWdhuMk9SxVBXrsWzk4K-0-zeebu2i1U0CSfx-9E0hD3dDtH7_ct96GXZl-tLOdV0oUWr3v30-9RJPORRK-oSX=w400-h39" width="400" /></a></div><h2 style="text-align: left;">Create a skill without any coding</h2><p></p><p style="clear: both; text-align: left;">We can now create a skill without a single line of code. Create sub-folders Skills/Baking with the following terminal commands:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="clear: both; text-align: left;"><span style="font-family: courier;">mkdir Skills</span></div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="clear: both; text-align: left;"><span style="font-family: courier;">cd Skills </span></div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="clear: both; text-align: left;"><span style="font-family: courier;">mkdir Baking</span></div></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="clear: both; text-align: left;"><span style="font-family: courier;">cd ..</span></div></blockquote></blockquote></blockquote><p style="clear: both;">Start the "Semantic Kernel" view in Visual Studio Code.</p><p style="clear: both;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjT7JPkohkYAUuzj9mMQNnpsAedNqnLdiH0BM9WWa8qLENrqUMUCRzedbLpf0wfCT2x4HIgpGmtHH5TIKl1-LTtXq64_lNffBif6hxBXITWBSTZUsl9CwFk1bXUkl70y0SLUL6xNYpIPGuD2Yvfk1fWN8MedG-Dfaa95i_AvTP4JgH6IWJWqU9aE6xcrADy" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="161" data-original-width="270" height="191" src="https://blogger.googleusercontent.com/img/a/AVvXsEjT7JPkohkYAUuzj9mMQNnpsAedNqnLdiH0BM9WWa8qLENrqUMUCRzedbLpf0wfCT2x4HIgpGmtHH5TIKl1-LTtXq64_lNffBif6hxBXITWBSTZUsl9CwFk1bXUkl70y0SLUL6xNYpIPGuD2Yvfk1fWN8MedG-Dfaa95i_AvTP4JgH6IWJWqU9aE6xcrADy" width="320" /></a></div>Click on "Add Semantic Skill" tool beside <i>Functions</i>.<div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg8Vife36EZW_OxFE6aRwKN9z3CmkoZj1QA6qvIpCi7-gKpDMcgCoAI6krxTK5HfVTf0qFHu9niYPJwFuDEaoqv6MoUMenvAFpU77s5GwFlIa_bu0-7cbW3m72KeYq-VvPp-njiTQvuWOWerEDbUGrf9jTre0UNv_pjaJn_gaY_Do1b7aWcW_8G39Aibyz1" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="94" data-original-width="522" height="73" src="https://blogger.googleusercontent.com/img/a/AVvXsEg8Vife36EZW_OxFE6aRwKN9z3CmkoZj1QA6qvIpCi7-gKpDMcgCoAI6krxTK5HfVTf0qFHu9niYPJwFuDEaoqv6MoUMenvAFpU77s5GwFlIa_bu0-7cbW3m72KeYq-VvPp-njiTQvuWOWerEDbUGrf9jTre0UNv_pjaJn_gaY_Do1b7aWcW_8G39Aibyz1=w400-h73" width="400" /></a></div><br />Click on "Create a new skill folder for the function".<p></p><p style="clear: both;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgavyHZmvo82nyIX-ebkbQhFh5rDNwUiLqjPJKcc6piOa1crCr09oSzF7X-rNpuxGih0vXsPXhqqI5Qip-hZMs0cedfWVm_dLBq7-Bp4ZJccTu0CNG8N6OKgMzYw_22rouFc2ZLUzFDwvNxpH3x8r9KGrLM5OnmuALhSpGam1CqMBRDXxC7VrZaJnJSxsFb" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="125" data-original-width="530" height="94" src="https://blogger.googleusercontent.com/img/a/AVvXsEgavyHZmvo82nyIX-ebkbQhFh5rDNwUiLqjPJKcc6piOa1crCr09oSzF7X-rNpuxGih0vXsPXhqqI5Qip-hZMs0cedfWVm_dLBq7-Bp4ZJccTu0CNG8N6OKgMzYw_22rouFc2ZLUzFDwvNxpH3x8r9KGrLM5OnmuALhSpGam1CqMBRDXxC7VrZaJnJSxsFb=w400-h94" width="400" /></a></div><br />Choose the <i>Skills/Baking</i> folder.<p></p><p style="clear: both;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjgGtz73zs7rbi_h2zOWYdZCCwpOG0mCOKU2f3TGDwdsPTdiWlg3gYE8g288jchyonqUK_97pBElGrdxMnNZYneJ4JyWafMuLiabqSKUxtEgfRZqLAEC19jnt1tklJ1lT1DZjdteVA1gHSJpDOdPyrXi51ECdZoVarSnQd-6va71ySx1R1NpCqEkdWvEZQE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="214" data-original-width="543" height="126" src="https://blogger.googleusercontent.com/img/a/AVvXsEjgGtz73zs7rbi_h2zOWYdZCCwpOG0mCOKU2f3TGDwdsPTdiWlg3gYE8g288jchyonqUK_97pBElGrdxMnNZYneJ4JyWafMuLiabqSKUxtEgfRZqLAEC19jnt1tklJ1lT1DZjdteVA1gHSJpDOdPyrXi51ECdZoVarSnQd-6va71ySx1R1NpCqEkdWvEZQE" width="320" /></a></div><p style="text-align: left;">Enter <i>CakeRecipe</i> for the function name.</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjpGfaIWbo5NCU6o4MVL6qLgP65QNgORjfSoG8LJllVrZEE8XWqgxipntZPl4TNKxx615QnWZUiVfxJbwbPtIv8Ur396eEMYbJQBfJwec3JSEgMV8S4SWtOKvoj_LpmWumPETvnAvqtwQdif4W31hI_BUNFMnITdG-3eJIY_OzICZUE4BSnPENffXGKno7w" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="104" data-original-width="791" height="85" src="https://blogger.googleusercontent.com/img/a/AVvXsEjpGfaIWbo5NCU6o4MVL6qLgP65QNgORjfSoG8LJllVrZEE8XWqgxipntZPl4TNKxx615QnWZUiVfxJbwbPtIv8Ur396eEMYbJQBfJwec3JSEgMV8S4SWtOKvoj_LpmWumPETvnAvqtwQdif4W31hI_BUNFMnITdG-3eJIY_OzICZUE4BSnPENffXGKno7w=w640-h85" width="640" /></a></div>A description for the function is required. Enter "Recipe for making a cake" for the description.<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgLTXtgPgydHG321DlQpwzLSIsGUOHV48yLQTXZxq5F9nxWU4n3U2bM9lhk66QmhXEejVatETrMf406wa9DAD-NrnThW4eO6GjfnNIhUKtuahIPGJWjuOS53ESnVFNqzGRCEmuvwTyUXftQVQtJRyia-FiQS4lQ5rGcaqdgQXN5EwRbAHxVXYa9UuTkd6RO" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="92" data-original-width="799" height="74" src="https://blogger.googleusercontent.com/img/a/AVvXsEgLTXtgPgydHG321DlQpwzLSIsGUOHV48yLQTXZxq5F9nxWU4n3U2bM9lhk66QmhXEejVatETrMf406wa9DAD-NrnThW4eO6GjfnNIhUKtuahIPGJWjuOS53ESnVFNqzGRCEmuvwTyUXftQVQtJRyia-FiQS4lQ5rGcaqdgQXN5EwRbAHxVXYa9UuTkd6RO=w640-h74" width="640" /></a></div>Two files get created in the Skills/Baking/CakeRecipe folder: <i>skprompt.txt</i> and <i>config.json</i>. <p></p><h4 style="text-align: left;">skprompt.txt</h4><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgS-0HnmlF53SyfAowT4wZ_NTakDvun_mbeda5Ifd8zvxcvlm9flT07sUVwCcDOOnbS53Pic0CnIhKabknWaTN-vwIzCgQNVxLIH2o950n50Wu-ziWK8yVWHo2mhHgy593Gb_33XLsYpF1XCBpLbQBQvG-YRXHL9flfFsWDABz-Wd8liVvQy52QqYHWJVFw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="234" data-original-width="502" height="186" src="https://blogger.googleusercontent.com/img/a/AVvXsEgS-0HnmlF53SyfAowT4wZ_NTakDvun_mbeda5Ifd8zvxcvlm9flT07sUVwCcDOOnbS53Pic0CnIhKabknWaTN-vwIzCgQNVxLIH2o950n50Wu-ziWK8yVWHo2mhHgy593Gb_33XLsYpF1XCBpLbQBQvG-YRXHL9flfFsWDABz-Wd8liVvQy52QqYHWJVFw=w400-h186" width="400" /></a></div><h4 style="text-align: left;">config.json</h4><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj8-vNbNxQe7yP_qqHXyspW8Z0gg4Og8XoIThwexvGzOJQSH2lhUUt0OvM5JBKIDTi42IqQSt1aBPeeIM9l476-3SIDJcyoICC11K8WCHxfr-dVGIWt1r-8kkTonGvr4azJQ5DGpHGK_34q2B5-mw7UZLOI1H3gWX2npx39zayyMpCh6lcbeWFlKnhL-V6R" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="518" data-original-width="655" height="317" src="https://blogger.googleusercontent.com/img/a/AVvXsEj8-vNbNxQe7yP_qqHXyspW8Z0gg4Og8XoIThwexvGzOJQSH2lhUUt0OvM5JBKIDTi42IqQSt1aBPeeIM9l476-3SIDJcyoICC11K8WCHxfr-dVGIWt1r-8kkTonGvr4azJQ5DGpHGK_34q2B5-mw7UZLOI1H3gWX2npx39zayyMpCh6lcbeWFlKnhL-V6R=w400-h317" width="400" /></a></div>Replace contents of <i>skprompt.txt </i>with the following:<p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">I want to bake a fabulous cake. Give me a recipe using the input provided. The cake must be easy, tasty, and cheap. I don't want to spend more than $10 on ingredients. I don't want to spend more than 30 minutes preparing the cake. I don't want to spend more than 30 minutes baking the cake. </span></p><p><span style="font-family: courier;">[INPUT]</span></p><p><span style="font-family: courier;">{{$input}}</span></p><p><span style="font-family: courier;">[END INPUT]</span></p></blockquote><div style="text-align: left;">The above file contains a prompt and a variable {{$input}}. The AI should give us a recipe for the type of cake that is entered to replace the {{$input}} variable.</div><h2 style="text-align: left;">Testing our baking skill</h2><p style="text-align: left;">Creating a skill with the Visual Studio Code 'Semantic Kernel Tool' is painless. We can now test our baking skill. Click on the arrow on the top-right of the panel.</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi9Pn6hlXXQJq1kXWeRcW7qXG5mJtnzc7g_t07JF-E09yYNXbyLcMHh2Aes0S2KlWKA592TlcOtxjCvl_ug5qY7pEBiYeqrp6XDmwX3YnV-NdzJ5rAagttdb7jp-skzl9qfr50l7R9An-0q6OuJ6MvZC33--HwgLvn9ZAnBDlN5o0WwjmU0EVm82TE4Clb6" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="300" data-original-width="922" height="208" src="https://blogger.googleusercontent.com/img/a/AVvXsEi9Pn6hlXXQJq1kXWeRcW7qXG5mJtnzc7g_t07JF-E09yYNXbyLcMHh2Aes0S2KlWKA592TlcOtxjCvl_ug5qY7pEBiYeqrp6XDmwX3YnV-NdzJ5rAagttdb7jp-skzl9qfr50l7R9An-0q6OuJ6MvZC33--HwgLvn9ZAnBDlN5o0WwjmU0EVm82TE4Clb6=w640-h208" width="640" /></a></div><br />You will be asked to enter a type of cake that you are interested in baking. I entered: <i>chocolate</i>.<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjpZipOxee4Hqyc3b0d-0wWZOkx22yxi3edRVsNnxaYQBpE6TTL1V7QKzH9SntuesZGk2iL9TM-0gtJ7ewQdPXV9Ero97a3q4qRVFldbF1czvVQeO4RB3EsRUY_36oycSh9gX9TLSkxSUdeeFNwnu81sC7tfJFA2PcjO1JcyTFTNmE9oqQqeMMNjzVUHbR4" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="127" data-original-width="864" height="94" src="https://blogger.googleusercontent.com/img/a/AVvXsEjpZipOxee4Hqyc3b0d-0wWZOkx22yxi3edRVsNnxaYQBpE6TTL1V7QKzH9SntuesZGk2iL9TM-0gtJ7ewQdPXV9Ero97a3q4qRVFldbF1czvVQeO4RB3EsRUY_36oycSh9gX9TLSkxSUdeeFNwnu81sC7tfJFA2PcjO1JcyTFTNmE9oqQqeMMNjzVUHbR4=w640-h94" width="640" /></a></div><br />If you check the OUTPUT tab at the bottom of Visual Studio Code, you will see the results.<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjLxuvDxxA6in_MhMcDz3_Va3c5kN3tZwhth4sX-U01iaKVT3uvpwBfF8ISYZIydCd3qMBDA3srzRFh_Mc_kRaWdI-SAJ4U9wc-bRCi2pIO2d3lvsAWkz2N9apm5gsMWIQ-L2qzTkjqpSGb9QfbgQ_R5B0rj_E5CLi9wel8htx2Xfg-xfU2IpWb3VscZxGr" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="342" data-original-width="805" height="272" src="https://blogger.googleusercontent.com/img/a/AVvXsEjLxuvDxxA6in_MhMcDz3_Va3c5kN3tZwhth4sX-U01iaKVT3uvpwBfF8ISYZIydCd3qMBDA3srzRFh_Mc_kRaWdI-SAJ4U9wc-bRCi2pIO2d3lvsAWkz2N9apm5gsMWIQ-L2qzTkjqpSGb9QfbgQ_R5B0rj_E5CLi9wel8htx2Xfg-xfU2IpWb3VscZxGr=w640-h272" width="640" /></a></div><br />The actual output I received was:<p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">AI Provider: AzureOpenAI</span></div><div style="text-align: left;"><span style="font-family: courier;">Model: text-davinci-003</span></div><div style="text-align: left;"><span style="font-family: courier;">Execute: Baking.CakeRecipe</span></div><div style="text-align: left;"><span style="font-family: courier;">Parameters:</span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>input: chocolate</span></span></div><div style="text-align: left;"><span style="font-family: courier;">Prompt:</span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>I want to bake a fabulous cake. Give me a recipe using the input</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>provided. The cake must be easy, tasty and cheap. I don't want to spend more than</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>$10 on ingredients. I don't want to spend more than 30 minutes preparing the</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>cake. I don't want to spend more than 30 minutes baking the cake. </span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>[INPUT]</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>chocolate</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>[END INPUT]</span></span></div><div style="text-align: left;"><span style="font-family: courier;">Result:</span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>Easy Chocolate Cake Recipe</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>Ingredients:</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- 1 ½ cups all-purpose flour</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- 1 cup granulated sugar</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- ¾ cup cocoa powder</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- 1 teaspoon baking soda</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- ½ teaspoon baking powder</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- ½ teaspoon salt</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- 2 eggs</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- 1 cup buttermilk</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- ½ cup vegetable oil</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>- 1 teaspoon vanilla extract</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>Instructions:</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>1. Preheat oven to 350°F. Grease and flour a 9-inch round cake pan.</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>2. In a large bowl, whisk together the flour, sugar, cocoa powder, baking soda, baking powder, and salt.</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>3. In a separate bowl, whisk together the eggs, buttermilk, oil, and vanilla extract.</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>4. Pour the wet ingredients into the dry ingredients and mix until just combined.</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>5. Pour the batter into the prepared cake pan and bake for 25-30 minutes, or until a toothpick inserted into the center comes out clean.</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>6. Allow the cake to cool in the pan for 10 minutes before transferring to a wire rack to cool completely.</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>7. Serve and enjoy!</span></span></div><div style="text-align: left;"><span style="font-family: courier;">Tokens:</span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>Input tokens: 88</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>Output tokens: 237</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>Total: 325</span></span></div><div style="text-align: left;"><span style="font-family: courier;">Duration:</span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="white-space: normal;"><span style="white-space: pre;"> </span>00:00:11.971</span></span></div><div style="text-align: left;"><span style="font-family: courier;">========== Function execution was finished. ==========</span></div></blockquote><h2 style="clear: both; text-align: left;">Using our baking skill in C# console app</h2><p style="clear: both; text-align: left;">Let us first create a console application in the root <i>sk-library</i> folder, with:</p><p style="clear: both; text-align: center;"><span style="font-family: courier;">dotnet new console</span></p><p style="clear: both; text-align: left;">We need to add two packages. One for SemanticKernel and the other is ConfigurationManager that allows us to read from settings in the App.config XML file.</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="clear: both; text-align: left;"><span style="font-family: courier;">dotnet add package Microsoft.SemanticKernel</span></div><div style="clear: both; text-align: left;"><span style="font-family: courier;">dotnet add package System.Configuration.ConfigurationManager</span></div></blockquote><p style="text-align: left;">Create a file named <i>App.config</i> in the root folder of the console application and add to it the important parameters that allow access to your Azure OpenAI service. Contents of <i>App.config</i> are like the following:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><?xml version="1.0"?></span></div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><configuration></span></div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <appSettings></span></div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <add key="endpoint" value="https://fake.openai.azure.com/" /></span></div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <add key="api-key" value="fakekey-fakekey-fakekey-fakekey" /></span></div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <add key="deployment-name" value="text-davinci-003" /></span></div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </appSettings></span></div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"></configuration></span></div></div></blockquote><p style="clear: both;">NOTE: Since I cannot share the endpoint and apiKey, I have fake values for these settings.</p><p style="clear: both;">Replace the code in <i>Program.cs</i> with the following code:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">using System.Configuration;</span></div></div><div><div><span style="font-family: courier;">using Microsoft.SemanticKernel;</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;">string _endpoint = ConfigurationManager.AppSettings["endpoint"]!;</span></div></div><div><div><span style="font-family: courier;">string _apikey = ConfigurationManager.AppSettings["api-key"]!;</span></div></div><div><div><span style="font-family: courier;">string _deploymentname = ConfigurationManager.AppSettings["deployment-name"]!;</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;">var builder = Kernel.CreateBuilder();</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;">builder.Services</span></div></div><div><div><span style="font-family: courier;"> .AddAzureOpenAITextGeneration(</span></div></div><div><div><span style="font-family: courier;"> _deploymentname</span></div></div><div><div><span style="font-family: courier;"> , _endpoint</span></div></div><div><div><span style="font-family: courier;"> , _apikey);</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;">var kernel = builder.Build();</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;">var functionDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Skills", "Baking");</span></div></div><div><div><span style="font-family: courier;">var semanticFunctions = kernel.ImportPluginFromPromptDirectory(functionDirectory);</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="color: #38761d; font-family: courier;">/* request user for input */</span></div></div><div><div><span style="font-family: courier;">Console.WriteLine("Enter a cake type you want to bake:");</span></div></div><div><div><span style="font-family: courier;">var cakeType = Console.ReadLine();</span></div></div><div><div><span style="font-family: courier;">var functionResult = await kernel.InvokeAsync(semanticFunctions["CakeRecipe"],</span></div></div><div><div><span style="font-family: courier;"> new KernelArguments {</span></div></div><div><div><span style="font-family: courier;"> { "input", cakeType }</span></div></div><div><div><span style="font-family: courier;"> });</span></div></div><div><div><span style="font-family: courier;">Console.WriteLine(functionResult);</span></div></div><div><div><span style="font-family: courier;">Console.WriteLine();</span></div></div></blockquote><p style="text-align: left;">Run the app with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet run</span></p><p style="text-align: left;">You will be asked to enter a type of cake. I entered: <i>lemon</i>.</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg0MgunJFM0NOkouuIvseqc0igzv2Z2Cnf2xLdS2MXsg9ULwEyKVQUCTZY0nO7z6DHgTUpgnIiAGE0YLRpsJDR03f4OiOmSQoueT1pShbb4UpWxmiVAd6uh7mYma7DOmdEZkj78bk3851ojBrzU_iA00dQwWKw8jj6V77Jf7FJIyy-kwSTy62dEHtBmPI_6" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="51" data-original-width="424" height="38" src="https://blogger.googleusercontent.com/img/a/AVvXsEg0MgunJFM0NOkouuIvseqc0igzv2Z2Cnf2xLdS2MXsg9ULwEyKVQUCTZY0nO7z6DHgTUpgnIiAGE0YLRpsJDR03f4OiOmSQoueT1pShbb4UpWxmiVAd6uh7mYma7DOmdEZkj78bk3851ojBrzU_iA00dQwWKw8jj6V77Jf7FJIyy-kwSTy62dEHtBmPI_6" width="320" /></a></div><br />This was the output given by the AI.<p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">Lemon Sponge Cake </span></div><div style="text-align: left;"><span style="font-family: courier;">Ingredients: </span></div><div style="text-align: left;"><span style="font-family: courier;">- 2 cups all-purpose flour </span></div><div style="text-align: left;"><span style="font-family: courier;">- 2 teaspoons baking powder </span></div><div style="text-align: left;"><span style="font-family: courier;">- ½ teaspoon salt </span></div><div style="text-align: left;"><span style="font-family: courier;">- 4 tablespoons butter </span></div><div style="text-align: left;"><span style="font-family: courier;">- 1 cup sugar </span></div><div style="text-align: left;"><span style="font-family: courier;">- 2 eggs </span></div><div style="text-align: left;"><span style="font-family: courier;">- 1 cup milk </span></div><div style="text-align: left;"><span style="font-family: courier;">- juice and zest of one lemon</span></div><div style="text-align: left;"><span style="font-family: courier;">Instructions: </span></div><div style="text-align: left;"><span style="font-family: courier;">1. Preheat oven to 350°F (175°C). Grease and flour an 8-inch cake pan.</span></div><div style="text-align: left;"><span style="font-family: courier;">2. In a medium bowl, sift together the flour, baking powder, and salt. </span></div><div style="text-align: left;"><span style="font-family: courier;">3. In a large bowl, beat the butter and sugar together until light and fluffy.</span></div><div style="text-align: left;"><span style="font-family: courier;">4. Beat in the eggs, one at a time. </span></div><div style="text-align: left;"><span style="font-family: courier;">5. Beat in the flour mixture alternately with the milk, beginning and ending with the flour mixture. </span></div><div style="text-align: left;"><span style="font-family: courier;">6. Stir in the lemon juice and zest. </span></div><div style="text-align: left;"><span style="font-family: courier;">7. Pour the batter into the prepared cake pan. </span></div><div style="text-align: left;"><span style="font-family: courier;">8. Bake for 25-30 minutes or until a toothpick inserted into the center comes out clean. </span></div><div style="text-align: left;"><span style="font-family: courier;">9. Allow the cake to cool in the pan before serving.</span></div></blockquote><p style="text-align: left;">You can build applications with a variety of AI skills. </p><p style="text-align: left;">Happy Coding.</p><p></p>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-12784824254591767802024-01-09T17:28:00.000-08:002024-01-10T16:00:50.315-08:00Getting started with 'Semantic Kernel Tool' extension in Visual Studio Code<p>In this article, let us explore the "Semantic Kernel Tools" extension for Visual Studio Code. We will simply run the C# "Hello World" startup chat-completion application that comes with the the tool. The main purpose of this tutorial is to help you configure and run your first C# Semantic Kernel app with the Visual Studio extension.</p><h2 style="text-align: left;"><b>What is Semantic Kernel?</b></h2><p>This is the official definition obtained from <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Create AI agents with Semantic Kernel | Microsoft Learn</a>:</p><p><i>Semantic Kernel is an open-source SDK that lets you easily build agents that can call your existing code. As a highly extensible SDK, you can use Semantic Kernel with models from OpenAI, Azure OpenAI, Hugging Face, and more!</i> </p><p>We now have an extension for Visual Studio Code that makes it very easy to build AI apps that use the large language models (LLMs) available through <a href="https://openai.com/" target="_blank">OpenAI</a>. </p><p>In order to proceed with this tutorial, you will need the following prerequisites:</p><p></p><ol style="text-align: left;"><li>.NET 8.0 Framework</li><li>Visual Studio Code</li><li>Access to Azure OpenAI</li><li>Install the 'Semantic Kernel Tool' extension into Visual Studio Code.</li></ol><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiMM459zEalZKsfgxyEtrWip1Q6WHGFpvsShtJFzvs52oK9jigEVsMfcuOTDP9wfs9cCyLL1ecS2ZnINL6YNcS_0CviXnM3DN8ZQMe4gkGN33ThetWJAaGIJNt3OZOxxOWa9-kvt-H2Z0iBBOcx2cb6K6r_373lEJNa5_CMBoGxzHLQIjF7wTVRsM0JQZOQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="468" data-original-width="1838" height="162" src="https://blogger.googleusercontent.com/img/a/AVvXsEiMM459zEalZKsfgxyEtrWip1Q6WHGFpvsShtJFzvs52oK9jigEVsMfcuOTDP9wfs9cCyLL1ecS2ZnINL6YNcS_0CviXnM3DN8ZQMe4gkGN33ThetWJAaGIJNt3OZOxxOWa9-kvt-H2Z0iBBOcx2cb6K6r_373lEJNa5_CMBoGxzHLQIjF7wTVRsM0JQZOQ=w640-h162" width="640" /></a></div><br /><h2 style="text-align: left;">Getting Started</h2></div><p style="text-align: left;">Once you have installed the 'Semantic Kernel Tool' extension, start Visual Studio Code. Click on <i>View</i> >> <i>Command Palette</i>:</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEijeZ2vgNy1t6xKq78eddDxMvx4p0hFVZ57oOiuWb4lNqZsp2Xv4PGK79Vo16ph9r3J1Dmf7Ld-gPMd0qxrDZTaZkoJWC1g2aQPYWLLWuR7FH1Xw2HPDF0KD-Xx7ug644Xf4ACzOZWVQGtutSKVdBvIC80C1M9o3T41ONnhvRGFJ1scRnblXw7egcgVRbTM" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="172" data-original-width="500" height="110" src="https://blogger.googleusercontent.com/img/a/AVvXsEijeZ2vgNy1t6xKq78eddDxMvx4p0hFVZ57oOiuWb4lNqZsp2Xv4PGK79Vo16ph9r3J1Dmf7Ld-gPMd0qxrDZTaZkoJWC1g2aQPYWLLWuR7FH1Xw2HPDF0KD-Xx7ug644Xf4ACzOZWVQGtutSKVdBvIC80C1M9o3T41ONnhvRGFJ1scRnblXw7egcgVRbTM" width="320" /></a></div><br />Select "Semantic Kernel: Create Project.<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgL03K6ctDdjKkwuusWz5U0xRUaG_gxSD-0SbzRJyTNVwzjFFDXOTOaL5xemC3zzv6uQ9oBsDvlrPeZKWRueVyDb3qNRaqFYOQ1pzb7BLaoCxaTF5sc516gCcJuiH_IXirCuebg1FWAv9f_vC2Yf_G9DuHCQP8MnA8NHFSrK02gJd32stYANSNM_YM8xSqW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="388" data-original-width="1020" height="122" src="https://blogger.googleusercontent.com/img/a/AVvXsEgL03K6ctDdjKkwuusWz5U0xRUaG_gxSD-0SbzRJyTNVwzjFFDXOTOaL5xemC3zzv6uQ9oBsDvlrPeZKWRueVyDb3qNRaqFYOQ1pzb7BLaoCxaTF5sc516gCcJuiH_IXirCuebg1FWAv9f_vC2Yf_G9DuHCQP8MnA8NHFSrK02gJd32stYANSNM_YM8xSqW" width="320" /></a></div><br />Choose "C# Hello World".<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgdL-GLZXZWDLErNEchlXZK-KrEv8bTQ-Iw78-_OJxbV--TDcODC2o_vBA4A2VvacH-i16uMOwf9Puiz5TLgpLlObMg_C0jxm0Tw0p-OLk0ZcL3cITQJIiU-mF1NIAU6cEfqWgORsftb3TSL8Uj16f9oBdsoQqB8j9TEqGhfgQFewSu9ZCXf6wrwIYMS44P" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="916" data-original-width="1894" height="310" src="https://blogger.googleusercontent.com/img/a/AVvXsEgdL-GLZXZWDLErNEchlXZK-KrEv8bTQ-Iw78-_OJxbV--TDcODC2o_vBA4A2VvacH-i16uMOwf9Puiz5TLgpLlObMg_C0jxm0Tw0p-OLk0ZcL3cITQJIiU-mF1NIAU6cEfqWgORsftb3TSL8Uj16f9oBdsoQqB8j9TEqGhfgQFewSu9ZCXf6wrwIYMS44P=w640-h310" width="640" /></a></div>Find a suitable working directory on your computer's file system, then click on the "Select location for new app" button.<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgoVGMCiItt4dd-AD8QJXaM-FViH4KpuqxcTiUu3cPshqGZN7REYDWWPb5UMhd9jCW7BRDcp6zSX2Lfzpt17YovvhCrd5xtoyNORapKbUafNf6lzgGUgN0YmrMCcKJyQ7zNhRQgooDpqd2RDA46r3i0VTE7mKB2PFrGfuXwo5pHE4Bi-hReMGaiHw_UbgEo" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="400" data-original-width="1586" height="162" src="https://blogger.googleusercontent.com/img/a/AVvXsEgoVGMCiItt4dd-AD8QJXaM-FViH4KpuqxcTiUu3cPshqGZN7REYDWWPb5UMhd9jCW7BRDcp6zSX2Lfzpt17YovvhCrd5xtoyNORapKbUafNf6lzgGUgN0YmrMCcKJyQ7zNhRQgooDpqd2RDA46r3i0VTE7mKB2PFrGfuXwo5pHE4Bi-hReMGaiHw_UbgEo=w640-h162" width="640" /></a></div><br />A new directory named <i>sk-csharp-hello-world</i> is created in your working directory. In Visual Studio Code, you will see the following directories and files:<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiakmOyQUBiTs9XPGLn11N5OBcpc6cVUlcjJly9PzvF-5BkrTt66L-KZ5Uv4yleAuh-ycFX-bnlRv6PmarAjYj-SUsR6AcnbhobhTPkX5o476HxRZdKmU3Wb5M6QQDLrxldyoqUjvBbqnlL69YIqQg_bpGdIEHLr8AhMDhLWOUatR4cCnHqXBQk9jVdoLPJ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="782" data-original-width="716" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEiakmOyQUBiTs9XPGLn11N5OBcpc6cVUlcjJly9PzvF-5BkrTt66L-KZ5Uv4yleAuh-ycFX-bnlRv6PmarAjYj-SUsR6AcnbhobhTPkX5o476HxRZdKmU3Wb5M6QQDLrxldyoqUjvBbqnlL69YIqQg_bpGdIEHLr8AhMDhLWOUatR4cCnHqXBQk9jVdoLPJ=w367-h400" width="367" /></a></div><br />Expand the config folder. You will see that there are two <i>appsettings.json</i> files - one for Azure-OpenAI and the other for OpenAI. <p></p><p style="text-align: left;"></p><p style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhx86OXqNhU_nyBI3BPcwcOyRrpC0ENnuNFZhg8U4JuHEzrJwoqGunMPTfX_LPAOS-amVLhm7wvlWsJgcdaDfg1MnLwok8vKW4U5BZWN4kCwLtuHWbxRa3QnPGMEKp3SDYtXGDrovjPumu2DLL4l2SJ8ALQ1e8Lt1d1gajoEqxM8lKBfAvHhJyYFEBboD6x" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="458" data-original-width="796" height="184" src="https://blogger.googleusercontent.com/img/a/AVvXsEhx86OXqNhU_nyBI3BPcwcOyRrpC0ENnuNFZhg8U4JuHEzrJwoqGunMPTfX_LPAOS-amVLhm7wvlWsJgcdaDfg1MnLwok8vKW4U5BZWN4kCwLtuHWbxRa3QnPGMEKp3SDYtXGDrovjPumu2DLL4l2SJ8ALQ1e8Lt1d1gajoEqxM8lKBfAvHhJyYFEBboD6x" width="320" /></a></p>Since we will be using with Azure-OpenAI, copy the file named "appsettings.json.azure-example" to another file simply named "appsettings.json".<p></p><p style="text-align: left;"></p><p style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg8l6zr2JR6vC5FXO0Xp4Wk4NXx3PMF9ssWcTHZt5OuSaiww-3rtHBIgvivk4soP-8aN4Fr-UhF9UQazR-8gXEV4aGH0BqJrML1lXFTNlbhPGHsPy3biPNCJHjJFXDFmxq2Y3t3sqDS0E0_ic4RqKQlRw_ogk2GRX381RjSqWV-6rUQ4sder6ndwAOJRJT4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="524" data-original-width="802" height="209" src="https://blogger.googleusercontent.com/img/a/AVvXsEg8l6zr2JR6vC5FXO0Xp4Wk4NXx3PMF9ssWcTHZt5OuSaiww-3rtHBIgvivk4soP-8aN4Fr-UhF9UQazR-8gXEV4aGH0BqJrML1lXFTNlbhPGHsPy3biPNCJHjJFXDFmxq2Y3t3sqDS0E0_ic4RqKQlRw_ogk2GRX381RjSqWV-6rUQ4sder6ndwAOJRJT4" width="320" /></a></p>Open <i>appsettings.json</i> in the editor.<p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">{</span></div><div style="text-align: left;"><span style="font-family: courier;"> "endpointType": "text-completion",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "serviceType": "AzureOpenAI",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "serviceId": "text-davinci-003",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "deploymentOrModelId": "text-davinci-003",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "endpoint": "https:// ... your endpoint ... .openai.azure.com/",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "apiKey": "... your Azure OpenAI key ..."</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p></p><p>We need to make an important adjustment to the <i>deploymentOrModelId</i> setting. The clue for what needs to be done comes from the <i>config/KernelSettings.cs</i> file. You will notice that it expects property names <i>deploymentId</i> and <i>modelId - </i>see lines 15 and 18 below:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgG7yqAykU0PjuBhQx5q6yOUz-bR8ti0RzNhMsAGFa9FvX_vpWH8r-qxY7DbngtxZvdU09OHuglur2sW8Rdyb41x4GPQKyhoTyTHCwEhl4Q1PzrHhMZzM_RqolwAfOEgwfJs2KRz7eL4ZF4uMMiXEJH9ZUN5y2FBlGzT0TVUTYbL4X0JgagL-2j8Ua3GUvy" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1014" data-original-width="1610" height="404" src="https://blogger.googleusercontent.com/img/a/AVvXsEgG7yqAykU0PjuBhQx5q6yOUz-bR8ti0RzNhMsAGFa9FvX_vpWH8r-qxY7DbngtxZvdU09OHuglur2sW8Rdyb41x4GPQKyhoTyTHCwEhl4Q1PzrHhMZzM_RqolwAfOEgwfJs2KRz7eL4ZF4uMMiXEJH9ZUN5y2FBlGzT0TVUTYbL4X0JgagL-2j8Ua3GUvy=w640-h404" width="640" /></a></div><br />Therefore, replace the <i>deploymentOrModelId</i> setting in <i>appsettings.json</i> with two settings <i>deploymentId</i> and <i>modelId. </i>Our <i>appsettings.json</i> now looks like this:<p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">{</span></div><div style="text-align: left;"><span style="font-family: courier;"> "endpointType": "text-completion",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "serviceType": "AzureOpenAI",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "serviceId": "text-davinci-003",</span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="background-color: #fcff01;">"deploymentId": "text-davinci-003",</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> <span style="background-color: #fcff01;">"modelId": "text-davinci-003",</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> "endpoint": "https:// ... your endpoint ... .openai.azure.com/",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "apiKey": "... your Azure OpenAI key ..."</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p>Of course, the next step is to use the proper values for <i>serviceId</i>, <i>deploymentId</i>, <i>modelId</i>, <i>endpoint</i>, and <i>apiKey</i>. This depends on the names of the various settings in your Azure-OpenAI account. Here is what I have in my Azure-OpenAI account:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj6AFo8PWA04or65GszoSh_Zx4tCF0Zd2JK4a-3tPJsNnJFCZjssKG-XtQTaTG4y1J1WtTiQVZrR5wBPjLxRaAxn_ZTrNTSN8Obc7WiO88OZMtkCa29Q7a8Q4UGzKvVdogZvtyE27OLt6OwaZH8ubNGC__S9YmpJvUpCXzYUpV8iYQns3SKTpU_mGir6hJv" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="520" data-original-width="715" height="466" src="https://blogger.googleusercontent.com/img/a/AVvXsEj6AFo8PWA04or65GszoSh_Zx4tCF0Zd2JK4a-3tPJsNnJFCZjssKG-XtQTaTG4y1J1WtTiQVZrR5wBPjLxRaAxn_ZTrNTSN8Obc7WiO88OZMtkCa29Q7a8Q4UGzKvVdogZvtyE27OLt6OwaZH8ubNGC__S9YmpJvUpCXzYUpV8iYQns3SKTpU_mGir6hJv=w640-h466" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgpqioqq938LWmxLifZ4LCe_ZdS3bk2Jl9Ela4zA6mLu7-MVxyHqx0WXdlewYAUdK8Jf9VxP6BcsePME2pm9d2leAS4_mYDH4TqM8RtHIOIuvR4cqI5nMHGC9E-qItUmTNpFBeSYsdGsoC19NJVLN_FsdX1YEMy-2Pf39enidZf6RNcMpogawkyq3YCAfBC" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="563" data-original-width="902" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEgpqioqq938LWmxLifZ4LCe_ZdS3bk2Jl9Ela4zA6mLu7-MVxyHqx0WXdlewYAUdK8Jf9VxP6BcsePME2pm9d2leAS4_mYDH4TqM8RtHIOIuvR4cqI5nMHGC9E-qItUmTNpFBeSYsdGsoC19NJVLN_FsdX1YEMy-2Pf39enidZf6RNcMpogawkyq3YCAfBC=w640-h400" width="640" /></a></div><br />The final state of my <i>appsettings.json</i> file is very similar to below. Since I cannot share the <i>endpoint</i> and <i>apiKey</i> with the world, I have fake values for these settings.<p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">{</span></div><div style="text-align: left;"><span style="font-family: courier;"> "endpointType": "text-completion",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "serviceType": "AzureOpenAI",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "serviceId": "gpt-3.5-turbo",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "deploymentId": "gpt-35-turbo",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "modelId": "gpt-35-turbo",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "endpoint": "https://fake.openai.azure.com/",</span></div><div style="text-align: left;"><span style="font-family: courier;"> "apiKey": "fakekey-fakekey-fakekey-fakekey"</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p>We can now run the application and see what it does. In a terminal window, enter:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet run</span></p><p>Here is the interaction I had with the application:</p><p><span style="font-family: courier;">% dotnet run</span></p><p><span style="font-family: courier;">User > <span style="background-color: #fcff01;">in the summertime</span></span></p><p><span style="font-family: courier;">Assistant > In the summertime, the weather is usually warm and sunny. It's a great time to enjoy outdoor activities like swimming, hiking, and barbecues. Many people also go on vacations or spend time at the beach. It's a season of relaxation and fun!</span></p><p><span style="font-family: courier;">User ></span> </p><p style="text-align: left;">The prompt that is central to the way the app works is found in <i>prompts/Chat.yaml</i>.</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">name: Chat</span></div></div><div><div><span style="font-family: courier;">template: |</span></div></div><div><div><span style="font-family: courier;"> <message role="system">You are a helpful assistant.</message></span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> {{#each messages}}</span></div></div><div><div><span style="font-family: courier;"> <message role="{{Role}}">{{~Content~}}</message></span></div></div><div><div><span style="font-family: courier;"> {{/each}}</span></div></div><div><div><span style="font-family: courier;">template_format: handlebars</span></div></div><div><div><span style="font-family: courier;">description: A function that uses the chat history to respond to the user.</span></div></div><div><div><span style="font-family: courier;">input_variables:</span></div></div><div><div><span style="font-family: courier;"> - name: messages</span></div></div><div><div><span style="font-family: courier;"> description: The history of the chat.</span></div></div><div><div><span style="font-family: courier;"> is_required: true</span></div></div></blockquote><p style="text-align: left;">Now that you were able to get the "Hello World" app working with the "Semantic Kernel Tool" extension for Visual Studio Code, go ahead and explore the other startup application types.</p><p style="text-align: left;">Good luck.</p><p style="text-align: left;"><br /></p><p></p>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-57489102101544693702024-01-02T15:27:00.000-08:002024-01-04T13:28:49.109-08:00Reading App.config XML file from .NET 8.0 Console and Web Applications<p>If you have worked with .NET before Core was released, you will be familiar with the <i>App.config</i> file. It is an XML settings file that was later replaced with JSON in .NET Core applications. If you still harbour a likeness to the XML <i>App.config</i> file, you will discover that it is still quite easy to use it for configuration settings instead of its JSON counterpart. In this article, we will read <i>App.config</i> settings from a .NET 8.0 console application and an ASP.NET 8.0 Razor Pages Application.</p><p>Companion Video: <a href="https://youtu.be/a_U4qNohkh0">https://youtu.be/a_U4qNohkh0</a></p><h2 style="text-align: left;">Setup Console Application</h2><p>In a suitable working folder, create a console application named <i>ReadAppConfig</i> with the following terminal window command:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet new console -o ReadAppConfig</span></p><p>Change into the <i>ReadingAppConfig</i> folder with:</p><p style="text-align: center;"><span style="font-family: courier;">cd ReadAppConfig</span></p><p>Install the <i>ConfigurationManager</i> package with the following command:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet add package System.Configuration.ConfigurationManager</span></p><h2 style="text-align: left;">The App.config file in Console Application</h2><p>Create a file named App.config in the root of your application and add to it the following XML content:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><?xml version="1.0"?></span></div><div style="text-align: left;"><span style="font-family: courier;"><configuration></span></div><div style="text-align: left;"><span style="font-family: courier;"> <appSettings></span></div><div style="text-align: left;"><span style="font-family: courier;"> <add key="endpoint" value="https://endpoint.somewhere.com/" /></span></div><div style="text-align: left;"><span style="font-family: courier;"> </appSettings></span></div><div style="text-align: left;"><span style="font-family: courier;"> <connectionStrings> </span></div><div style="text-align: left;"><span style="font-family: courier;"> <add</span></div><div style="text-align: left;"><span style="font-family: courier;"> name="sqlServer"</span></div><div style="text-align: left;"><span style="font-family: courier;"> providerName="System.Data.SqlClient"</span></div><div style="text-align: left;"><span style="font-family: courier;"> connectionString="Data Source=localhost;Initial Catalog=MyDB;Integrated Security=True;" /></span></div><div style="text-align: left;"><span style="font-family: courier;"> </connectionStrings></span></div><div style="text-align: left;"><span style="font-family: courier;"></configuration></span></div></blockquote><p>The above file contains the following important settings:</p><p></p><ol style="text-align: left;"><li>an application setting named "endpoint" with value "https://endpoint.somewhere.com/"</li><li>a database connection string named "sqlServer" with value"Data Source=localhost;Initial Catalog=MyDB;Integrated Security=True;"</li></ol><h2 style="text-align: left;">Program.cs in Console Application</h2><p style="text-align: left;">Replace the contents of <i>Program.cs</i> with the following C# code:</p><p></p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><span style="font-family: courier;">using System.Configuration;</span></div></blockquote><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div><span style="font-family: courier;">string _endpoint = ConfigurationManager.AppSettings["endpoint"]!;</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">string _connectionString = ConfigurationManager.ConnectionStrings["sqlServer"]!.ConnectionString;</span></div></div></blockquote><div style="text-align: left;"><div><span style="font-family: courier;"><br /></span></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div><span style="font-family: courier;">Console.WriteLine($"Endpoint: {_endpoint}");</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">Console.WriteLine($"Connection String: {_connectionString}"); </span> </div></div></blockquote><h2 style="text-align: left;">Run Console Application</h2><p style="text-align: left;">In a terminal window, run the app with the following command:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet run</span></p><p style="text-align: left;">You should see the following output that verifies that information from App.config has successfully been read:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">Endpoint: https://endpoint.somewhere.com/</span></div><div style="text-align: left;"><span style="font-family: courier;">Connection String: Data Source=localhost;Initial Catalog=MyDB;Integrated Security=True;</span></div></blockquote><p style="text-align: left;"></p><hr style="border: 1px solid red;" /><p></p><h2 style="text-align: left;">ASP.NET Razor Pages Application</h2><p style="text-align: left;">Next, let us do the same thing in an ASP.NET Razor Pages Application.</p><p style="text-align: left;">Exit the previous console application and create a razor pages application is a separate folder with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet new razor -o ReadAppConfigWeb</span></p><p style="text-align: left;">Change to the folder containing the Razor Pages app with:</p><p style="text-align: center;"><span style="font-family: courier;">cd ReadAppConfigWeb </span></p><p style="text-align: left;">Add the ConfigurationManager package with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet add package System.Configuration.ConfigurationManager</span></p><p style="text-align: left;">In the root folder of your ASP.NET Razor Pages application, add the same <i>App.config</i> file as before:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><?xml version="1.0"?></span></div><div style="text-align: left;"><span style="font-family: courier;"><configuration></span></div><div style="text-align: left;"><span style="font-family: courier;"> <appSettings></span></div><div style="text-align: left;"><span style="font-family: courier;"> <add key="endpoint" value="https://endpoint.somewhere.com/" /></span></div><div style="text-align: left;"><span style="font-family: courier;"> </appSettings></span></div><div style="text-align: left;"><span style="font-family: courier;"> <connectionStrings> </span></div><div style="text-align: left;"><span style="font-family: courier;"> <add</span></div><div style="text-align: left;"><span style="font-family: courier;"> name="sqlServer"</span></div><div style="text-align: left;"><span style="font-family: courier;"> providerName="System.Data.SqlClient"</span></div><div style="text-align: left;"><span style="font-family: courier;"> connectionString="Data Source=localhost;Initial Catalog=MyDB;Integrated Security=True;" /></span></div><div style="text-align: left;"><span style="font-family: courier;"> </connectionStrings></span></div><div style="text-align: left;"><span style="font-family: courier;"></configuration></span></div></blockquote><p>Open <i>Pages/Index.cshtml.cs</i> in your favourite editor and make the following changes to it:</p><p>1) Import <i>System.Configuration</i> with:</p><p style="text-align: center;"><span style="font-family: courier;">using Config = System.Configuration;</span></p><p>2) Add the following instance variables to the <i>IndexModel </i>class:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">string _endpoint = Config.ConfigurationManager.AppSettings["endpoint"]!;</span></p><p><span style="font-family: courier;">string _connectionString = Config.ConfigurationManager.ConnectionStrings["sqlServer"]!.ConnectionString;</span></p></blockquote><p>3) Append these lines of code to the constructor:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">_logger.LogInformation($"Endpoint: {_endpoint}");</span></p><p><span style="font-family: courier;">_logger.LogInformation($"Connection String: {_connectionString}");</span></p></blockquote><p style="text-align: left;">Now we can run the application with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></p><p>In the terminal window you will notice the following output, which shows that the setting have indeed been read from <i>App.config</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">Endpoint: https://endpoint.somewhere.com.com/</span></p><p><span style="font-family: courier;">Connection String: Data Source=localhost;Initial Catalog=MyDB;Integrated Security=True;</span></p></blockquote><h2 style="text-align: left;">Conclusion</h2><p>When using .NET Core, you can always resort to saving your configuration setting in an XML file instead of JSON. </p>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-44391191857450359722023-12-13T21:17:00.000-08:002023-12-14T14:30:05.699-08:00Give your ChatBot personality with Azure OpenAI and C#We will create a .NET 8.0 chatbot console application that uses the ChatGPT natural language model. This will be done using Azure OpenAI. The chatbot will have a distinct personality which will be reflected in its response.<div><br /></div><div>Source Code: <a href="https://github.com/medhatelmasry/BotWithPersonality">https://github.com/medhatelmasry/BotWithPersonality</a><br /><h2 style="text-align: left;">Prerequisites</h2><div>You will need the following to continue:</div><div><ul style="text-align: left;"><li>.NET 8 SDK</li><li>A C# code editor such as Visual Studio Code</li><li>An Azure subscription with access to the OpenAI Service</li></ul></div><div><h2 style="text-align: left;">Getting started with Azure OpenAI service</h2><div>To follow this tutorial, you will create an Azure OpenAI service under your Azure subscription. Follow these steps:</div><div><br /></div><div>Navigate to the Azure portal at https://portal.azure.com/. </div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiYSaW63r1asoEGxo6H6bqoIVMm2QFL-SZSNbb1BwvswsgxMnBeupYAJ1AbcHjPZR0dmgYurOAUD9X3guF923t42ZGnWhW1qeaKKcAFphxUhr3QVSGhNZRquSpAffZO1di0y_FoxsekB0LZ5ApFSavo0DTa6W_LTR7_h6X9sM9dU9niz0ATZJX6co4lNrXh" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="511" data-original-width="762" height="215" src="https://blogger.googleusercontent.com/img/a/AVvXsEiYSaW63r1asoEGxo6H6bqoIVMm2QFL-SZSNbb1BwvswsgxMnBeupYAJ1AbcHjPZR0dmgYurOAUD9X3guF923t42ZGnWhW1qeaKKcAFphxUhr3QVSGhNZRquSpAffZO1di0y_FoxsekB0LZ5ApFSavo0DTa6W_LTR7_h6X9sM9dU9niz0ATZJX6co4lNrXh" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Click on “Create a resource”.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgHhfX0r4y_Cg-tmBYJYlF6p41mrIcfk-bgnUAw72fk3tumDCF9OLYQvt1bbfjPo1bg2K8dDH-vV0WmkExljzEuwt2igbw61RnL9hg3hlK7E_QHVJYm62sXV3QdfZq8jUGho1cVl3dOIceTUkD5CtIBM1Fzrh83HDYtr5QUcNsfH3sevSD329jScC3FzBuG" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="219" data-original-width="813" height="86" src="https://blogger.googleusercontent.com/img/a/AVvXsEgHhfX0r4y_Cg-tmBYJYlF6p41mrIcfk-bgnUAw72fk3tumDCF9OLYQvt1bbfjPo1bg2K8dDH-vV0WmkExljzEuwt2igbw61RnL9hg3hlK7E_QHVJYm62sXV3QdfZq8jUGho1cVl3dOIceTUkD5CtIBM1Fzrh83HDYtr5QUcNsfH3sevSD329jScC3FzBuG" width="320" /></a></div><br />Enter “openai” in the filter then select “openai”.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi-QzSLeMzucsO4st9vQGG8zrbzi7CCxnbUdLt2PgQZLUVlAGZY13mC4Wl2D2vSH9zSJk-ZDzBvvz-4jrRALFVATAAY88nrvXa_z361_ntv0gb2oyJ2Fjg3OuR01MfEiQLbu6gb2W8tJmh7Kzc9CQJG37B3tiojqRKWObCN5DoTLfwFA5ZJMaxrFHwEbURW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="389" data-original-width="677" height="184" src="https://blogger.googleusercontent.com/img/a/AVvXsEi-QzSLeMzucsO4st9vQGG8zrbzi7CCxnbUdLt2PgQZLUVlAGZY13mC4Wl2D2vSH9zSJk-ZDzBvvz-4jrRALFVATAAY88nrvXa_z361_ntv0gb2oyJ2Fjg3OuR01MfEiQLbu6gb2W8tJmh7Kzc9CQJG37B3tiojqRKWObCN5DoTLfwFA5ZJMaxrFHwEbURW" width="320" /></a></div><br /><div class="separator" style="clear: both;">Choose your subscription then create a new resource group. In my case (as shown above), I created a new resource group named “OpenAI-RG”.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhdKJKaM4clp1bKgq6RC8lrDdJW1VIqQGElpyY-9ntHlJCGDXZjjyx9yAS8TGXKpdx8xWizxqY4z2OeNTIjr-_Jo-6k9GmzwHlKBLmvqVK-TzPuXVqshCqbmpA4amGCGq5m_GbT67EbP3fNqFmjIIGAby8RTuPNjtm4hGNzHtgXhhGqWuk2zWUYMhmk-TQt" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="541" data-original-width="640" height="541" src="https://blogger.googleusercontent.com/img/a/AVvXsEhdKJKaM4clp1bKgq6RC8lrDdJW1VIqQGElpyY-9ntHlJCGDXZjjyx9yAS8TGXKpdx8xWizxqY4z2OeNTIjr-_Jo-6k9GmzwHlKBLmvqVK-TzPuXVqshCqbmpA4amGCGq5m_GbT67EbP3fNqFmjIIGAby8RTuPNjtm4hGNzHtgXhhGqWuk2zWUYMhmk-TQt=w640-h541" width="640" /></a></div><div><br /></div><div>Continue with the selection of a region, provide a instance name (mze-openai in the example above) and select the “Standard S0” pricing tier. Click on the <i>Next</i> button.</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh6B-yIG3_fmBTN4Cio_-khHW6sAVjOd2vqvwKgQY8N8lP4ts_-kKmy6g5kTtN7vZBQO9_Esrm_nF4wnchfuWbqeukIhIjlVB--5wOJaew2AAfHX4yiwnKEgVmYJhXwueKb8rZJuJnDKu81RBtBP0hD4R0BLURH1-2V3kqEb7yJaIJGIilztb88MylNdZwG" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="181" data-original-width="400" height="181" src="https://blogger.googleusercontent.com/img/a/AVvXsEh6B-yIG3_fmBTN4Cio_-khHW6sAVjOd2vqvwKgQY8N8lP4ts_-kKmy6g5kTtN7vZBQO9_Esrm_nF4wnchfuWbqeukIhIjlVB--5wOJaew2AAfHX4yiwnKEgVmYJhXwueKb8rZJuJnDKu81RBtBP0hD4R0BLURH1-2V3kqEb7yJaIJGIilztb88MylNdZwG=w400-h181" width="400" /></a></div><div><div><br /></div><div>Accept the default (All networks, including the internet, can access this resource.) on the <i>Network</i> tab then click on the <i>Next</i> button.</div></div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEir6af3B-l256O2mWqB6vaQVphvQi_cqiflrNTt8c86WoCzrEQF8jD3EZR9TR2DtiBDuxecBSDBQzviw1ekzK2DJX--YQisPaZGr-LGaBg8u9iq7B2kVLwIkpne7bJsoQ9h0QSERLl6mgC4MOJDVQjfJEIWw3cdF3oPTtEHcS5tIgYgeNoLOWVpJ1vpFbrC" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="140" data-original-width="398" height="141" src="https://blogger.googleusercontent.com/img/a/AVvXsEir6af3B-l256O2mWqB6vaQVphvQi_cqiflrNTt8c86WoCzrEQF8jD3EZR9TR2DtiBDuxecBSDBQzviw1ekzK2DJX--YQisPaZGr-LGaBg8u9iq7B2kVLwIkpne7bJsoQ9h0QSERLl6mgC4MOJDVQjfJEIWw3cdF3oPTtEHcS5tIgYgeNoLOWVpJ1vpFbrC=w400-h141" width="400" /></a></div><div><br /></div><div>On the <i>Tags</i> tab, click on <i>Next</i> without making any changes.</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiCiANF7ODOmD2swG1ueHokY8Lo2wLzjqEdknYwyibJ6k4C8PcgFBDqTqQ3yCnQqWhgjBWtSOGB7PrcMAndKbd2zMQvvgDDgu8GAsebQf8j8axyH7RYxeSS4g2Hr0bq1JpW-cG1ehYWpw1ZF2kVvYiE0s0AQc_5Q8_Obm8-SYfV4JmzsKv4RCwToDKc2hum" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="362" data-original-width="400" height="580" src="https://blogger.googleusercontent.com/img/a/AVvXsEiCiANF7ODOmD2swG1ueHokY8Lo2wLzjqEdknYwyibJ6k4C8PcgFBDqTqQ3yCnQqWhgjBWtSOGB7PrcMAndKbd2zMQvvgDDgu8GAsebQf8j8axyH7RYxeSS4g2Hr0bq1JpW-cG1ehYWpw1ZF2kVvYiE0s0AQc_5Q8_Obm8-SYfV4JmzsKv4RCwToDKc2hum=w640-h580" width="640" /></a></div><div><br /></div><div>Click the <i>Create</i> button on the “Review + submit” tab. Deployment takes about one minute. </div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjisL4T6NzB1MmysgQtiU_j3TwqiUEj_Cmx9FoVz4lgggNIZERmwC1jyjRqehCrPaZCFwgwbDiXZ2wV9cGhxz96Lu_-rCh9m9PPZpctTziLhYkF5kIxOHeafxFxZ6hx4kvsnONmTCB8OSQPLKUVPp5QqLmPizUeoxBqRf-cDVkwNUJeaV2dXzDO2_uv8pmR" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="300" data-original-width="640" height="300" src="https://blogger.googleusercontent.com/img/a/AVvXsEjisL4T6NzB1MmysgQtiU_j3TwqiUEj_Cmx9FoVz4lgggNIZERmwC1jyjRqehCrPaZCFwgwbDiXZ2wV9cGhxz96Lu_-rCh9m9PPZpctTziLhYkF5kIxOHeafxFxZ6hx4kvsnONmTCB8OSQPLKUVPp5QqLmPizUeoxBqRf-cDVkwNUJeaV2dXzDO2_uv8pmR=w640-h300" width="640" /></a></div><div><br /></div><div>On the <i>Overview</i> blade, click on “Keys and Endpoint” in the left side navigation.</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgvdJ7qbnkeuY-LYcb_DcissivLiW4PjZZfGWruOEIjLCPQSSdtZLj1TZHTizzMCf_ghGlPgfUpqZsNOpHLf4mW_Q-EBhlJQwEzYLNSrKv2UpN2CmogPwDnfnwKJWljezzJQiFEh9qJn1rdofBEooAubg8zuEs0Mtfq5bsvQaIvwHooqJ9tZHLsq4T7VINQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="230" data-original-width="400" height="230" src="https://blogger.googleusercontent.com/img/a/AVvXsEgvdJ7qbnkeuY-LYcb_DcissivLiW4PjZZfGWruOEIjLCPQSSdtZLj1TZHTizzMCf_ghGlPgfUpqZsNOpHLf4mW_Q-EBhlJQwEzYLNSrKv2UpN2CmogPwDnfnwKJWljezzJQiFEh9qJn1rdofBEooAubg8zuEs0Mtfq5bsvQaIvwHooqJ9tZHLsq4T7VINQ=w400-h230" width="400" /></a></div><br /><div>Copy <i>KEY 1</i> and <i>Endpoint</i> then save the values in a text editor like Notepad.</div><div><br /></div><div>We will need to create a model deployment that we can use for text completion. To do this, return to the Overview tab.</div></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhP0VSRrMYYJptiybcK4Xg7ASWB8TCBF32E5FQzBOyPvMBn212HFFkXSKmF7bJHJr_M5UnYveVM9GCb1pKKLyXzvdf6UVASo1ZL4ovJmG-7MvFTTXIPCVHlHRBlOg9bS7RC2Z1Q9TWciTSl-10iUa6WspP07wSeO0bVtlec5VyK12AGRzbzHlX5yewOPN9i" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="145" data-original-width="400" height="232" src="https://blogger.googleusercontent.com/img/a/AVvXsEhP0VSRrMYYJptiybcK4Xg7ASWB8TCBF32E5FQzBOyPvMBn212HFFkXSKmF7bJHJr_M5UnYveVM9GCb1pKKLyXzvdf6UVASo1ZL4ovJmG-7MvFTTXIPCVHlHRBlOg9bS7RC2Z1Q9TWciTSl-10iUa6WspP07wSeO0bVtlec5VyK12AGRzbzHlX5yewOPN9i=w640-h232" width="640" /></a></div><br /><div><div>Open “Go to Azure OpenAI Studio” in a new browser tab.</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh9fYz0iCA1vDFlz-NsMMepgIYaB8L9TcMrgho3p0wI81_q8jvuUw-yyy4t2BTnn9fpDnXE3m6KMft31mIIVSjEuU57JevqbAjJdf2HtHX46Zf2XcK5K6OWEhzeJHZoc-CTVMx82NRmGb-zbaWSEACsLgmKaAZhSPp4GihXHNuq-Mrp2eOYxygKgiFZyYPg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="101" data-original-width="640" height="102" src="https://blogger.googleusercontent.com/img/a/AVvXsEh9fYz0iCA1vDFlz-NsMMepgIYaB8L9TcMrgho3p0wI81_q8jvuUw-yyy4t2BTnn9fpDnXE3m6KMft31mIIVSjEuU57JevqbAjJdf2HtHX46Zf2XcK5K6OWEhzeJHZoc-CTVMx82NRmGb-zbaWSEACsLgmKaAZhSPp4GihXHNuq-Mrp2eOYxygKgiFZyYPg=w640-h102" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">Click on “Create new deployment”.</div><div><br /></div><div></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhqhoeNRM_ZYJ4UIEYJAwiEPTpdO5VJSFwv6F1jyq1B66cCxxmFB5q-xiFMN4qTR47R2GldTtI0P1ajRkHEXMzvK-HIre8z7eq8y4ap01-NqEWj0hAOt0e9fot7ZZQOREIBerui40_lIstN5csvUm4orrFsWTe3N94q1Fma5gn4asDlQlW7OrFZcANj8Q8J" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="153" data-original-width="400" height="153" src="https://blogger.googleusercontent.com/img/a/AVvXsEhqhoeNRM_ZYJ4UIEYJAwiEPTpdO5VJSFwv6F1jyq1B66cCxxmFB5q-xiFMN4qTR47R2GldTtI0P1ajRkHEXMzvK-HIre8z7eq8y4ap01-NqEWj0hAOt0e9fot7ZZQOREIBerui40_lIstN5csvUm4orrFsWTe3N94q1Fma5gn4asDlQlW7OrFZcANj8Q8J=w400-h153" width="400" /></a></div><br /><div>Click on “+ Create new deployment”.</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjWTkokYd0jzdF4P0qmI9ABnQsE9K9OM3DgSjIZmfaEWrfKaRXtuQ7rGq0_c2IAAeXIQN3bSl9Nty756-KrOgRBoYWDt8R-J8orNrleVLntVQJYnz78cEoFdYiE66f-V2RdmaxpenMj6ui-fskzAiCmn3ZcVCjpNNs4PkeFKfuFzdHox4jeHP7hyEPw_0Cm" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="271" data-original-width="399" height="271" src="https://blogger.googleusercontent.com/img/a/AVvXsEjWTkokYd0jzdF4P0qmI9ABnQsE9K9OM3DgSjIZmfaEWrfKaRXtuQ7rGq0_c2IAAeXIQN3bSl9Nty756-KrOgRBoYWDt8R-J8orNrleVLntVQJYnz78cEoFdYiE66f-V2RdmaxpenMj6ui-fskzAiCmn3ZcVCjpNNs4PkeFKfuFzdHox4jeHP7hyEPw_0Cm=w400-h271" width="400" /></a></div><div><br /></div><div>For the model, select “gpt-35-turbo” and give the deployment a name which you need to remember as this will be configured in the app that we will soon develop. I called the deployment name <i>gpt35-turbo-deployment</i>. Click on the <i>Create</i> button.</div><div><br /></div><div>As a summary, we will need the following parameters in our application:</div></div><div><br /></div>
<table><tbody><tr><th>Setting</th><th>Value</th></tr><tr><td>KEY 1:</td><td>this-is-a-fake-api-key</td></tr><tr><td>Endpoint:</td><td>https://mze-openai.openai.azure.com/</td></tr><tr><td>Model deployment:</td><td>gpt35-turbo-deployment</td></tr></tbody></table>
</div></div></div>
<p>Next, we will create our console application.</p><h2 style="text-align: left;">Console Application</h2><div>Create a console application with .NET 8.0:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">dotnet new console -f net8.0 -o BotWithPersonality</span></div></div><div><div><span style="font-family: courier;">cd BotWithPersonality</span></div></div></blockquote><div><br /></div><div>Add these two packages:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><span style="font-family: courier;">dotnet add package Azure.AI.OpenAI -v 1.0.0-beta.11</span></div><div><span style="font-family: courier;">dotnet add package Microsoft.Extensions.Configuration.Json -v 8.0.0</span></div></blockquote><p> </p><h2 style="text-align: left;">Configuration Settings</h2><div>The first package is for Azure OpenAI. The second package will help us read configuration settings from the <i>appsettings.json</i> file.</div><div><br /></div><div>Create a file named <i>apsettings.json</i> and add to the following:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">{</span></div></div><div><div><span style="font-family: courier;"> "settings": {</span></div></div><div><div><span style="font-family: courier;"> "deployment-name": "gpt35-turbo-deployment",</span></div></div><div><div><span style="font-family: courier;"> "endpoint": "https://mze-openai.openai.azure.com/",</span></div></div><div><div><span style="font-family: courier;"> "key": "this-is-a-fake-api-key"</span></div></div><div><div><span style="font-family: courier;"> }</span></div></div><div><div><span style="font-family: courier;">}</span></div></div></blockquote><div><br /></div><div><div>When our application gets built and packaged, we want this file to get copied to the output directory. Therefore, we need to add the following XML to the <i>.csproj</i> file just before the closing <i></Project></i> tag.</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;"><ItemGroup></span></div></div><div><div><span style="font-family: courier;"> <None Include="*.json" CopyToOutputDirectory="PreserveNewest" /></span></div></div><div><div><span style="font-family: courier;"></ItemGroup> </span></div></div></blockquote><div><br /></div><div>In order to read the settings from <i>appsettings.json</i>, we need to create a helper method. Add a class named <i>Utils.cs</i> and add to it the following code to it:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">public class Utils {</span></div></div><div><div><span style="font-family: courier;"> public static string GetConfigValue(string config) {</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> IConfigurationBuilder builder = new ConfigurationBuilder();</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> if (System.IO.File.Exists("appsettings.json"))</span></div></div><div><div><span style="font-family: courier;"> builder.AddJsonFile("appsettings.json", false, true);</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> if (System.IO.File.Exists("appsettings.Development.json"))</span></div></div><div><div><span style="font-family: courier;"> builder.AddJsonFile("appsettings.Development.json", false, true);</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> IConfigurationRoot root = builder.Build();</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> return root[config]!;</span></div></div><div><div><span style="font-family: courier;"> }</span></div></div><div><div><span style="font-family: courier;">}</span></div></div></blockquote><div><br /></div><div>As an example, if we want to read the <i>endpoint</i> value in <i>appsettings.json</i>, we can use the following statement:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">Utils.GetConfigValue("settings:endpoint")</span></div><h2 style="text-align: left;">Building our ChatBot app</h2><div>Delete whatever code there is in <i>Program.cs</i> and add these <i>using</i> statements at the top:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">using Azure;</span></div></div><div><div><span style="font-family: courier;">using Azure.AI.OpenAI;</span></div></div><div><div><span style="font-family: courier;">using </span><span style="font-family: courier;">BotWithPersonality</span><span style="font-family: courier;">;</span></div></div><div><br /></div></blockquote>Let us first read the settings we need from <i>appsettings.json</i>. Therefore, append this code to <i>Program.cs</i>:<br /><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><br /></div><div><div><span style="font-family: courier;">string ENDPOINT = Utils.GetConfigValue("settings:endpoint");</span></div></div><div><div><span style="font-family: courier;">string KEY = Utils.GetConfigValue("settings:key");</span></div></div><div><div><span style="font-family: courier;">string DEPLOYMENT_NAME = Utils.GetConfigValue("settings:deployment-name");</span></div></div></blockquote><div><br /></div><div>Next, let us give our chatbot a personality. We will tell Azure OpenAI that our chatbot has the personality of a developer from Newfouldland in Eastern Canada. Append this constant to the <i>Program.cs</i>:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">const string SYSTEM_MESSAGE </span></div></div><div><div><span style="font-family: courier;"> = """</span></div></div><div><div><span style="font-family: courier;"> You are a friendly assistant named DotNetBot. </span></div></div><div><div><span style="font-family: courier;"> You prefer to use Canadian Newfoundland English as your language and are an expert in the .NET runtime </span></div></div><div><div><span style="font-family: courier;"> and C# and F# programming languages.</span></div></div><div><div><span style="font-family: courier;"> Response using Newfoundland colloquialisms and slang.</span></div></div><div><div><span style="font-family: courier;"> """;</span></div></div></blockquote><div><div> </div></div><div>Create a new <i>OpenAIClient</i> by appending the following code to <i>Program.cs</i>:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">var openAiClient = new OpenAIClient(</span></div></div><div><div><span style="font-family: courier;"> new Uri(ENDPOINT),</span></div></div><div><div><span style="font-family: courier;"> new AzureKeyCredential(KEY)</span></div></div><div><div><span style="font-family: courier;">);</span></div></div></blockquote><div><br /></div><div>We will next define our <i>ChatCompletionsOptions</i> with a starter user message "Introduce yourself". Append this code to <i>Program.cs</i>:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">var chatCompletionsOptions = new ChatCompletionsOptions</span></div></div><div><div><span style="font-family: courier;">{</span></div></div><div><div><span style="font-family: courier;"> DeploymentName = DEPLOYMENT_NAME, // Use DeploymentName for "model" with non-Azure clients</span></div></div><div><div><span style="font-family: courier;"> Messages =</span></div></div><div><div><span style="font-family: courier;"> {</span></div></div><div><div><span style="font-family: courier;"> new ChatRequestSystemMessage(SYSTEM_MESSAGE),</span></div></div><div><div><span style="font-family: courier;"> new ChatRequestUserMessage("Introduce yourself"),</span></div></div><div><div><span style="font-family: courier;"> }</span></div></div><div><div><span style="font-family: courier;">};</span></div></div></blockquote><div><br /></div><div><div>The <i>ChatCompletionsOptions</i> object is aware of the deployment model name and keeps track of the conversation between the user and the chatbot. Note that there are two chat messages pre-filled before the conversation even starts. One chat message is from the <i>System</i> (SYSTEM_MESSAGE) and gives the chat model instructions on what kind of chatbot it is supposed to be. In this case, we told the chat model to act like somebody from Newfoundland, Canada. Then we told the chatbot to introduce itself by adding a message as <i>User</i> saying "Introduce yourself.".</div><div><br /></div><div>Now that we have set up the <i>OpenAIClient</i>, and <i>ChatCompletionsOptions</i>, we can start calling the APIs. Append the following code to <i>Program.cs</i> to finalize the chatbot:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">while (true)</span></div></div><div><div><span style="font-family: courier;">{</span></div></div><div><div><span style="font-family: courier;"> Console.WriteLine();</span></div></div><div><div><span style="font-family: courier;"> Console.Write("DotNetBot: ");</span></div></div><div><div><span style="font-family: courier;"> </span></div></div><div><div><span style="font-family: courier;"> Response<ChatCompletions> chatCompletionsResponse = await openAiClient.GetChatCompletionsAsync(</span></div></div><div><div><span style="font-family: courier;"> chatCompletionsOptions</span></div></div><div><div><span style="font-family: courier;"> );</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> var chatMessage = chatCompletionsResponse.Value.Choices[0].Message;</span></div></div><div><div><span style="font-family: courier;"> Console.WriteLine($"[{chatMessage.Role.ToString().ToUpperInvariant()}]: {chatMessage.Content}");</span></div></div><div><div><span style="font-family: courier;"> </span></div></div><div><div><span style="font-family: courier;"> chatCompletionsOptions.Messages.Add(new ChatRequestUserMessage(chatMessage.Content));</span></div></div><div><div><span style="font-family: courier;"> </span></div></div><div><div><span style="font-family: courier;"> Console.WriteLine();</span></div></div><div><div><span style="font-family: courier;"> </span></div></div><div><div><span style="font-family: courier;"> Console.Write("Enter a message: ");</span></div></div><div><div><span style="font-family: courier;"> var userMessage = Console.ReadLine();</span></div></div><div><div><span style="font-family: courier;"> chatCompletionsOptions.Messages.Add(new ChatRequestUserMessage(userMessage));</span></div></div><div><div><span style="font-family: courier;">}</span></div></div></blockquote><div><br /></div><div><div>Inside an infinite loop, the <i>chatCompletionsOptions</i> is passed to the <i>openAiClient.GetChatCompletionsAsync</i> method.</div><div><br /></div><div>The response from the chat model is then written to the console and added to the chat history stored in <i>chatCompletionsOptions</i>.</div><div><br /></div><div>Now the user is prompted to say anything, which will then be added to the chat history as well. Thereafter, the next iteration of the loop starts, which sends the chat history back to the chat model in Azure OpenAI. One can terminate the endless loop by hitting CTRL+C on the keyboard.</div><div><br /></div><div>Let us run the app and see how it performs.</div></div><div><br /></div><div>Run the app with:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet run</span></div><div><br /></div><div>Here is an example of the ongoing dialog:</div><div><br /></div><div><div><span style="font-family: courier;">DotNetBot: [ASSISTANT]: How's she going, buddy? I'm DotNetBot, your trusty virtual assistant from the Rock. I'm here to lend you a hand with all things .NET and programming in C# and F#. So whether you need help with some code or just wanna have a chin wag about tech, I'm your go-to. Don't be shy now, fire away with your questions!</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">Enter a message: <span style="background-color: #fcff01;">How do you spell color?</span></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">DotNetBot: [ASSISTANT]: Sure ting, me ol' pal! In Canadian Newfoundland English, we like to spell "color" as "colour." It's a wee bit different than how they spell it down in other parts of the world, but we like to keep things unique and full of character around here. So, if you're ever writin' some code and need to use that word, remember to toss in that extra "u" for good measure. Happy codin', me son!</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">Enter a message: </span></div></div><div><br /></div><div>I asked the question "How do you spell color?" and it rambled on about how it is done in Newfoundland. Quite amusing....</div><div><br /></div><div>Hope this was useful.</div><div><br /></div>
<div></div>
</div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-77130156794622662122023-12-11T20:56:00.000-08:002024-02-01T08:22:30.108-08:00Built-in authentication with ASP.NET Core .NET 8.0 Minimal WebAPI<p>In this tutorial I will showcase the built-in WebApi authentication that was introduced with ASP.NET Core 8.0. To keep it simple, we shall persist our data in the lightweight SQLite database.</p><p>Source Code: <a href="https://github.com/medhatelmasry/WebApiAuth">https://github.com/medhatelmasry/WebApiAuth</a></p><p>Companion Video: <a href="https://youtu.be/SSLSNWRQJiA">https://youtu.be/SSLSNWRQJiA</a></p><h2 style="text-align: left;">Prerequisites</h2><div>It is assumed that you have installed the following on your computer:</div><ul style="text-align: left;"><li>.NET 8.0</li><li>Visual Studio Code editor</li></ul><h2 style="text-align: left;">Getting Started</h2><p>In a suitable working directory, create a new WebApi application using the following terminal window command:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet new webapi -f net8.0 --name WebApiAuth</span></p><p>Change directory with:</p><p style="text-align: center;"><span style="font-family: courier;">cd WebApiAuth</span></p><p>We will need to add some packages. Also in the same terminal window, run the following commands:</p><p><span style="font-family: courier;">dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore -v 8.0.0<br />dotnet add package Microsoft.EntityFrameworkCore.Sqlite -v 8.0.0<br />dotnet add package Microsoft.EntityFrameworkCore.Tools -v 8.0.0<br />dotnet add package Microsoft.EntityFrameworkCore.Design -v 8.0.0</span></p><p>The above packages allow us to use SQLite and Entity Framework.</p><p>Make sure you have the dotnet-ef tool. If you do not, you can globally install it with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet tool install --global dotnet-ef</span></p><p>If you already have the dotnet-ef, update your version to the latest with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet tool update --global dotnet-ef</span></p><p>Let us run our web app and have a peek at what it does. Run the web app with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></p><p>This gets loaded into your default browser:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhVGVaKQ_la20HFkisZec714y0HRzVVvvo4jTzGR1q79Ejjs17bOqMKEVEISI11ZUZQw-Vywmppor8D3aoJI6y4WOMjdLZX7eo6BfZoCfJ_oP5MMWmo9GAnqSOgnElTeoYSEsGIlvTdQynF67W78paqzyWxTWU4T8BMeuvVE3aHzviG10bu_Hp4s4sqlGPg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="768" data-original-width="992" height="310" src="https://blogger.googleusercontent.com/img/a/AVvXsEhVGVaKQ_la20HFkisZec714y0HRzVVvvo4jTzGR1q79Ejjs17bOqMKEVEISI11ZUZQw-Vywmppor8D3aoJI6y4WOMjdLZX7eo6BfZoCfJ_oP5MMWmo9GAnqSOgnElTeoYSEsGIlvTdQynF67W78paqzyWxTWU4T8BMeuvVE3aHzviG10bu_Hp4s4sqlGPg=w400-h310" width="400" /></a></div><br />Click on GET >> Try it out >> Execute. This will produce the following output:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj9Ou2IvVTykxJdesixEg1yqZIsfiOQhLjF1cE6Mp4bCKDmvck3ziKWgoLkkqdGTULCWkzPWDSR1FPzVvW2TKAxiwVrCc4w6cLkK2Z06FTLE0Wd-9MhACQYoqs_UmO0a7EfT439UNvgdmxRcW_863hG6i69gq8N46-cPRwdGVJT0MJfKVDLMh4QsGWwgNnb" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="494" data-original-width="396" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEj9Ou2IvVTykxJdesixEg1yqZIsfiOQhLjF1cE6Mp4bCKDmvck3ziKWgoLkkqdGTULCWkzPWDSR1FPzVvW2TKAxiwVrCc4w6cLkK2Z06FTLE0Wd-9MhACQYoqs_UmO0a7EfT439UNvgdmxRcW_863hG6i69gq8N46-cPRwdGVJT0MJfKVDLMh4QsGWwgNnb=w320-h400" width="320" /></a></div><br />It is time for us to peek into the code. Stop the server with CTRL C, then start Visual Studio Code with the following terminal command:<p></p><p style="text-align: center;"><span style="font-family: courier;">code .</span></p><h2 style="text-align: left;">Database context class</h2><p>Since we will be using Entity Framework to talk to the SQLite database, we will need a database context class. Add a folder named <i>Data</i>, then add to it a class named <i>ApplicationDbContext</i> that derives from <i>IdentityDbContext<IdentityUser></i> with the following code:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">using Microsoft.AspNetCore.Identity;</span></div><div style="text-align: left;"><span style="font-family: courier;">using Microsoft.AspNetCore.Identity.EntityFrameworkCore;</span></div><div style="text-align: left;"><span style="font-family: courier;">using Microsoft.EntityFrameworkCore;</span></div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><div style="text-align: left;"><span style="font-family: courier;">namespace WebApiAuth.Data;</span></div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><div style="text-align: left;"><span style="font-family: courier;">public class ApplicationDbContext: IdentityDbContext<IdentityUser> {</span></div><div style="text-align: left;"><span style="font-family: courier;"> public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)</span></div><div style="text-align: left;"><span style="font-family: courier;"> {}</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p style="text-align: left;">Add the following connection string to <i>appsettings.json</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">"ConnectionStrings": {</span></div><div style="text-align: left;"><span style="font-family: courier;"> "DefaultConnection": "Data Source=webapi-auth.db;"</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p style="text-align: left;">Next, we must register <i>ApplicationDbContext</i> with our app by adding the following code into <i>Program.cs</i> right before 'var app = builder.Build();':</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// Configure identity database access via EF Core</span></span></div><div style="text-align: left;"><span style="font-family: courier;">var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");</span></div><div style="text-align: left;"><span style="font-family: courier;">builder.Services.AddDbContext<ApplicationDbContext>(options =></span></div><div style="text-align: left;"><span style="font-family: courier;"> options.UseSqlite(connectionString));</span></div></blockquote><h2 style="text-align: left;">Other required services</h2><p>Add the following code right after the above code:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// Authorization</span></span></div><div style="text-align: left;"><span style="font-family: courier;">builder.Services.AddAuthorization();</span></div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// Activate identity APIs. By default, both cookies and proprietary tokens</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// are activated. Cookies will be issued based on the 'useCookies' querystring</span></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="color: #38761d;">// parameter in the login endpoint</span></span></div><div style="text-align: left;"><span style="font-family: courier;">builder.Services.AddIdentityApiEndpoints<IdentityUser>()</span></div><div style="text-align: left;"><span style="font-family: courier;"> .AddEntityFrameworkStores<ApplicationDbContext>();</span></div></blockquote><div style="text-align: left;"><span style="font-family: courier;"></span></div><p style="text-align: left;">We need to add authorization and authentication middleware with the following code right after 'app.UseHttpsRedirection();':</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">app.UseAuthentication(); </span></div><div style="text-align: left;"><span style="font-family: courier;">app.UseAuthorization(); </span></div></blockquote><p style="text-align: left;">Let us secure the <i>/weatherforecast </i>endpoint by forcing authentication. Chain the following to the endpoint by add ing this code right under '.WithOpenApi()':</p><p style="text-align: center;"><span style="font-family: courier;">.RequireAuthorization();</span></p><p style="text-align: left;">The full <i>app.MapGet() </i>code will look like this:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">app.MapGet("/weatherforecast", () =></span></div><div style="text-align: left;"><span style="font-family: courier;">{</span></div><div style="text-align: left;"><span style="font-family: courier;"> var forecast = Enumerable.Range(1, 5).Select(index =></span></div><div style="text-align: left;"><span style="font-family: courier;"> new WeatherForecast</span></div><div style="text-align: left;"><span style="font-family: courier;"> (</span></div><div style="text-align: left;"><span style="font-family: courier;"> DateOnly.FromDateTime(DateTime.Now.AddDays(index)),</span></div><div style="text-align: left;"><span style="font-family: courier;"> Random.Shared.Next(-20, 55),</span></div><div style="text-align: left;"><span style="font-family: courier;"> summaries[Random.Shared.Next(summaries.Length)]</span></div><div style="text-align: left;"><span style="font-family: courier;"> ))</span></div><div style="text-align: left;"><span style="font-family: courier;"> .ToArray();</span></div><div style="text-align: left;"><span style="font-family: courier;"> return forecast;</span></div><div style="text-align: left;"><span style="font-family: courier;">})</span></div><div style="text-align: left;"><span style="font-family: courier;">.WithName("GetWeatherForecast")</span></div><div style="text-align: left;"><span style="font-family: courier;">.WithOpenApi()</span></div><div style="text-align: left;"><span style="background-color: #fcff01; font-family: courier;">.RequireAuthorization();</span></div></blockquote><h2 style="text-align: left;">Adding Identity API endpoints</h2><p style="text-align: left;">Add the identity endpoints to your app by calling <i>MapIdentityApi<IdentityUser>()</i>. Add the following code to <i>Program.cs</i> right before 'app.Run();':</p><p style="text-align: center;"><span style="font-family: courier;">app.MapIdentityApi<IdentityUser>();</span></p><h2 style="text-align: left;">Migrations</h2><p style="text-align: left;">Since our app uses EF Core, we will create a migration in the Data folder and update the database. Run the following terminal commands:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">dotnet ef migrations add M1 -o Data/Migrations</span></p><p><span style="font-family: courier;">dotnet ef database update</span></p></blockquote><p>You will notice that a SQLite database file is created named <i>webapi-auth.db</i>.</p><h2 style="text-align: left;">Try it out</h2><p>Let us test our application to see whether or not we have indeed succeeded in securing our API endpoint. Start your app with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></p><p>You will see a variety of identity related endpoints when the following gets loaded into your default browser:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi6wmR3Cv6LcEf4fnXFAEkIbxvQL6W2fZQ2gJxAtS8HDRxm7VtEoMC4nMrDNzOEny-_-R4vQhF_lSBYEVC9k9LvZbq30wXPeh8Mp5MyT_Ezm_yU5EnXLRDm8Ys-QpS7RrOt_b14GUaHd0XdUg0kpPuGOkfMHs05I0l25qrunVwTXPUqx9KBYVSJb323_8MJ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="912" data-original-width="1000" height="365" src="https://blogger.googleusercontent.com/img/a/AVvXsEi6wmR3Cv6LcEf4fnXFAEkIbxvQL6W2fZQ2gJxAtS8HDRxm7VtEoMC4nMrDNzOEny-_-R4vQhF_lSBYEVC9k9LvZbq30wXPeh8Mp5MyT_Ezm_yU5EnXLRDm8Ys-QpS7RrOt_b14GUaHd0XdUg0kpPuGOkfMHs05I0l25qrunVwTXPUqx9KBYVSJb323_8MJ=w400-h365" width="400" /></a></div><br />Try to hit the <i>/weatherforecast </i>endpoint and see the data. You will encounter a 401 (unauthorized) error:<br /><br /><div><p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhuK2DXL1YMFLpfnXUiDLwtSALOiLRt2AZboM2ezKSG7cLcGxPXV8hGnGhM5GKVBZWqEsVEL_eRa-uETFnyoDFgJ06CqX8FT7zX174-rVq42h_x6RJc6E3d57WJ6-g1P9rUNKP5vvskj_Bclv3GJ8iuT49XfJHRYKsxw2oMWnSpok0hIPHytRXkrb1Gu-R1" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="335" data-original-width="418" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhuK2DXL1YMFLpfnXUiDLwtSALOiLRt2AZboM2ezKSG7cLcGxPXV8hGnGhM5GKVBZWqEsVEL_eRa-uETFnyoDFgJ06CqX8FT7zX174-rVq42h_x6RJc6E3d57WJ6-g1P9rUNKP5vvskj_Bclv3GJ8iuT49XfJHRYKsxw2oMWnSpok0hIPHytRXkrb1Gu-R1" width="299" /></a></div><p></p><p>Let us register a user. Click on the <i>/register</i> endpoint then click on the "<i>Try it out</i>" button. Update the JSON object so it looks like this:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">{</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> "email": "a@a.a",</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> "password": "P@$$w0rd"</span></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote></blockquote><p>Click on the <i>Execute</i> button. You will get a Code 200 response representing success:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjrcHmnz7ZlBoAl53CTRemIACzVNDj8-Cy9o11UITPEkT163AIFdrslpSRcuzSLx-0IJSIh0OkqEKVac6Pfd4ESIYUMyTO9_s4OJ7IkWKRr82Jjdl03ZRgAHMZvL9s4_RzJizUgQwNiGRTcqBQO30hSYpm6QMkS9ffWncAs5PeHTy6MXr6rgfhAYWEOJ21M" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="262" data-original-width="409" height="205" src="https://blogger.googleusercontent.com/img/a/AVvXsEjrcHmnz7ZlBoAl53CTRemIACzVNDj8-Cy9o11UITPEkT163AIFdrslpSRcuzSLx-0IJSIh0OkqEKVac6Pfd4ESIYUMyTO9_s4OJ7IkWKRr82Jjdl03ZRgAHMZvL9s4_RzJizUgQwNiGRTcqBQO30hSYpm6QMkS9ffWncAs5PeHTy6MXr6rgfhAYWEOJ21M" width="320" /></a></div><br />Next, let us login with the credentials we created. Click on the <i>/login </i>endpoint, then click on the "Try it out" button.<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi71uBahG9l0LzwFyxneb39N4k3mBHoZxAqHvis6hWQxCc6cl-ugLAeUqvFZJWQcX9H9FemW3lGHyTbZ2YQh9ZFqgmFNy-3zuqv7WggS1N0lGF92mhoiDLS-QtnL80IHD-EMNEh3yFzXPGqR8ybVfGntLZqhMCMAIuyRxnXPS6c4ISvV-uBG6ifWAlcFIWg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="473" data-original-width="467" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEi71uBahG9l0LzwFyxneb39N4k3mBHoZxAqHvis6hWQxCc6cl-ugLAeUqvFZJWQcX9H9FemW3lGHyTbZ2YQh9ZFqgmFNy-3zuqv7WggS1N0lGF92mhoiDLS-QtnL80IHD-EMNEh3yFzXPGqR8ybVfGntLZqhMCMAIuyRxnXPS6c4ISvV-uBG6ifWAlcFIWg=w395-h400" width="395" /></a></div><br />Choose <i>true</i> for <i>useCookies</i> and update the JSON object so it only has the credentials we had previously created. Thereafter, click on the <i>Execute</i> button. You should get a code 200 response:<p></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEguE-I47xbFgoU97S0j7upDaUIEqL2a9rTwANc9PtPfUhGzBEuZH_2hGfLq7qVpd6cGhVHl7nbpbSf5fdAm3Lzm7fLL5Lcv-hTEdhmCCP0l-YD_Pr3JrFCSclHh33WrU08PyckroCS__VMRnnxiEk2bRk4cuucSYEn_r21lLnuAg_l3OKB_JZEyHqbzOLtE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="310" data-original-width="441" height="225" src="https://blogger.googleusercontent.com/img/a/AVvXsEguE-I47xbFgoU97S0j7upDaUIEqL2a9rTwANc9PtPfUhGzBEuZH_2hGfLq7qVpd6cGhVHl7nbpbSf5fdAm3Lzm7fLL5Lcv-hTEdhmCCP0l-YD_Pr3JrFCSclHh33WrU08PyckroCS__VMRnnxiEk2bRk4cuucSYEn_r21lLnuAg_l3OKB_JZEyHqbzOLtE" width="320" /></a></div>Now let's try getting the data using the GET <i>/weatherforecast </i>endpoint. It should be a relief that we can now see the data.<p></p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhMoAzSU7BvG4gVcW16WEq2w0ibcZ53kwr5zytVbmN-cg1SYr-4zTyz9xnHhyiFeluBPQvjkG4iKxVcHNbut8tP-qHeVEVXR-KSIYwxkjwh9C5oaa9khk8tkTY5L7F16Wd7ee6iQtPTWUsJaF7z8GEnzNZLQJtIVZGRkxzMIopOpo5mW8vtqG0QBz8QI6r2" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="815" data-original-width="979" height="533" src="https://blogger.googleusercontent.com/img/a/AVvXsEhMoAzSU7BvG4gVcW16WEq2w0ibcZ53kwr5zytVbmN-cg1SYr-4zTyz9xnHhyiFeluBPQvjkG4iKxVcHNbut8tP-qHeVEVXR-KSIYwxkjwh9C5oaa9khk8tkTY5L7F16Wd7ee6iQtPTWUsJaF7z8GEnzNZLQJtIVZGRkxzMIopOpo5mW8vtqG0QBz8QI6r2=w640-h533" width="640" /></a></div><br />With ASP.NET Core 8.0 came an easier way for securing WebAPI. </div></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-65660328723816357362023-12-11T15:33:00.000-08:002024-01-08T21:26:31.494-08:00Mix Blazor with ASP.NET Core MVC<p>In this tutorial I will show you how you can inject Blazor components into your regular ASP.NET Core MVC web application. We will integrate the free QuickGrid Blazor components into our MVC application. <br /></p><p>Source code: <a href="https://github.com/medhatelmasry/MvcWithBlazor">https://github.com/medhatelmasry/MvcWithBlazor</a></p><p>Companion video: <a href="https://youtu.be/dsoGW59Vtpk">https://youtu.be/dsoGW59Vtpk</a></p><h2>Prerequisites</h2><div>It is assumed that you have installed the following on your computer:</div><ul><li>.NET 8.0</li><li>Visual Studio Code editor</li></ul><h2 style="text-align: left;">Getting Started</h2><p>In a suitable working directory, create an ASP.NET MVC 8.0 web application with the following terminal window command:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet new mvc -f net8.0 --name MvcWithBlazor</span></p><p>Change into the new directory with:</p><p style="text-align: center;"><span style="font-family: courier;">cd </span><span style="font-family: courier;">MvcWithBlazor</span></p><p style="text-align: left;">Add the following Blazor <i>QuickGrid</i> package with this terminal window command:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet add package Microsoft.AspNetCore.Components.QuickGrid -v 8.0.0</span></p><div style="text-align: left;">Start Visual Studio Code with:<p></p><p style="text-align: center;"><span style="font-family: courier;">code .</span></p></div><p style="text-align: left;"><span style="font-family: courier;"><span style="font-family: Times;">To keep it simple, we will create some hard-coded student data. Create a <i>Student.cs</i> class file inside the <i>Models</i> folder with the following code:</span></span></p><div style="text-align: left;"><span style="font-family: courier;">namespace MvcWithBlazor.Models;<br />public class Student<br />{<br /> required public int? Id { get; set; }<br /> required public string FirstName { get; set; }<br /> required public string LastName { get; set; }<br /> required public string School { get; set; }<br /> public static IQueryable<Student> GetStudents()<br /> {<br /> int ndx = 0;<br /> List<Student> students = new List<Student>() {<br /> new Student() { Id = ++ndx, FirstName="Max", LastName="Pao", School="Science" },<br /> new Student() { Id = ++ndx, FirstName="Tom", LastName="Fay", School="Mining" },<br /> new Student() { Id = ++ndx, FirstName="Ann", LastName="Sun", School="Nursing" },<br /> new Student() { Id = ++ndx, FirstName="Joe", LastName="Fox", School="Computing" },<br /> new Student() { Id = ++ndx, FirstName="Sue", LastName="Mai", School="Mining" },<br /> new Student() { Id = ++ndx, FirstName="Ben", LastName="Lau", School="Business" },<br /> new Student() { Id = ++ndx, FirstName="Zoe", LastName="Ray", School="Mining" },<br /> new Student() { Id = ++ndx, FirstName="Sam", LastName="Ash", School="Medicine" },<br /> new Student() { Id = ++ndx, FirstName="Dan", LastName="Lee", School="Computing" },<br /> new Student() { Id = ++ndx, FirstName="Pat", LastName="Day", School="Science" },<br /> new Student() { Id = ++ndx, FirstName="Kim", LastName="Rex", School="Computing" },<br /> new Student() { Id = ++ndx, FirstName="Tim", LastName="Ram", School="Business" },<br /> new Student() { Id = ++ndx, FirstName="Rob", LastName="Wei", School="Mining" },<br /> new Student() { Id = ++ndx, FirstName="Jan", LastName="Tex", School="Science" },<br /> new Student() { Id = ++ndx, FirstName="Jim", LastName="Kid", School="Business" },<br /> new Student() { Id = ++ndx, FirstName="Ben", LastName="Chu", School="Medicine" },<br /> new Student() { Id = ++ndx, FirstName="Mia", LastName="Tao", School="Computing" },<br /> new Student() { Id = ++ndx, FirstName="Ted", LastName="Day", School="Business" },<br /> new Student() { Id = ++ndx, FirstName="Amy", LastName="Roy", School="Science" },<br /> new Student() { Id = ++ndx, FirstName="Ian", LastName="Kit", School="Nursing" },<br /> new Student() { Id = ++ndx, FirstName="Liz", LastName="Tan", School="Medicine" },<br /> new Student() { Id = ++ndx, FirstName="Mat", LastName="Roy", School="Tourism" },<br /> new Student() { Id = ++ndx, FirstName="Deb", LastName="Luo", School="Medicine" },<br /> new Student() { Id = ++ndx, FirstName="Ana", LastName="Poe", School="Computing" },<br /> new Student() { Id = ++ndx, FirstName="Lyn", LastName="Raj", School="Science" },<br /> new Student() { Id = ++ndx, FirstName="Amy", LastName="Ash", School="Tourism" },<br /> new Student() { Id = ++ndx, FirstName="Kim", LastName="Kid", School="Mining" },<br /> new Student() { Id = ++ndx, FirstName="Bec", LastName="Fry", School="Nursing" },<br /> new Student() { Id = ++ndx, FirstName="Eva", LastName="Lap", School="Computing" },<br /> new Student() { Id = ++ndx, FirstName="Eli", LastName="Yim", School="Business" },<br /> new Student() { Id = ++ndx, FirstName="Sam", LastName="Hui", School="Science" },<br /> new Student() { Id = ++ndx, FirstName="Joe", LastName="Jin", School="Mining" },<br /> new Student() { Id = ++ndx, FirstName="Liz", LastName="Kuo", School="Agriculture" },<br /> new Student() { Id = ++ndx, FirstName="Ric", LastName="Mak", School="Tourism" },<br /> new Student() { Id = ++ndx, FirstName="Pam", LastName="Day", School="Computing" },<br /> new Student() { Id = ++ndx, FirstName="Stu", LastName="Gad", School="Business" },<br /> new Student() { Id = ++ndx, FirstName="Tom", LastName="Bee", School="Tourism" },<br /> new Student() { Id = ++ndx, FirstName="Bob", LastName="Lam", School="Agriculture" },<br /> new Student() { Id = ++ndx, FirstName="Jim", LastName="Ots", School="Medicine" },<br /> new Student() { Id = ++ndx, FirstName="Tom", LastName="Mag", School="Mining" },<br /> new Student() { Id = ++ndx, FirstName="Hal", LastName="Doe", School="Agriculture" },<br /> new Student() { Id = ++ndx, FirstName="Roy", LastName="Kim", School="Nursing" },<br /> new Student() { Id = ++ndx, FirstName="Vis", LastName="Cox", School="Science" },<br /> new Student() { Id = ++ndx, FirstName="Kay", LastName="Aga", School="Tourism" },<br /> new Student() { Id = ++ndx, FirstName="Reo", LastName="Hui", School="Business" },<br /> new Student() { Id = ++ndx, FirstName="Bob", LastName="Roe", School="Medicine" },<br /> };<br /> return students.AsQueryable();<br /> }<br />}</span></div><p style="text-align: left;">Create a <i>Components</i> folder and add to it the following files with their respective contents:</p><h4 style="text-align: left;"><span style="font-family: courier;"><span style="font-family: Times;">_Imports.razor</span></span></h4><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div><span style="font-family: courier;">@using System.Net.Http</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">@using System.Net.Http.Json</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">@using Microsoft.AspNetCore.Components.Forms</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">@using Microsoft.AspNetCore.Components.Routing</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">@using Microsoft.AspNetCore.Components.Web</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">@using static Microsoft.AspNetCore.Components.Web.RenderMode</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">@using Microsoft.AspNetCore.Components.Web.Virtualization</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">@using Microsoft.JSInterop</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">@using </span><span style="background-color: #fcff01; font-family: courier; text-align: center;">MvcWithBlazor</span></div></div><div style="text-align: left;"><div><span style="font-family: courier;">@using </span><span style="background-color: #fcff01; font-family: courier; text-align: center;">MvcWithBlazor</span><span style="font-family: courier;">.Components</span> </div></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div><span style="font-family: courier;">@using Microsoft.AspNetCore.Components.QuickGrid</span></div></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">@using <span style="background-color: #fcff01;">MvcWithBlazor</span>.Models</span></div></blockquote><div style="text-align: left;"><h4 style="text-align: left;">Routes.razor</h4></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><Router AppAssembly="@typeof(Program).Assembly"></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <Found Context="routeData"></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <RouteView RouteData="@routeData" /></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <FocusOnNavigate RouteData="@routeData" Selector="h1" /></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> </Found></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"></Router></span></div></div></blockquote><div style="text-align: left;"><h4 style="text-align: left;">App.razor</h4></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><!DOCTYPE html></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><html lang="en"></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><head></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <meta charset="utf-8" /></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <meta name="viewport" content="width=device-width, initial-scale=1.0" /></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <base href="/" /></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <link rel="stylesheet" href="</span><span style="background-color: #fcff01; font-family: courier; text-align: center;">MvcWithBlazor</span><span style="font-family: courier;">.styles.css" /></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <HeadOutlet /></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"></head></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><body></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <Routes /></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> <script src="_framework/blazor.web.js"></script></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"></body></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"></html></span></div></div></blockquote><div style="text-align: left;"><p style="text-align: left;">Note that you need adjust the above highlighted text with your application name.</p><p style="text-align: left;">Inside the <i>Components</i> folder, add a sub-folder named <i>Pages</i>. Inside the <i>Components/Pages </i>folder add a <i>Students.razor </i>component file with this code:</p><div><span style="font-family: courier;">@page "/students"<br />@rendermode InteractiveServer<br /><PageTitle>Students</PageTitle><br /><h1>Students</h1><br /><QuickGrid Items="@students" Pagination="@pagination"><br /> <PropertyColumn Property="@(_ => _.Id)" Sortable="true" /><br /> <TemplateColumn Title="Name" SortBy="@sortByName"><br /> <div class="flex items-center"><br /> <nobr><br /> <strong>@context.FirstName @context.LastName</strong><br /> </nobr><br /> </div><br /> </TemplateColumn><br /> <PropertyColumn Property="@(_ => _.School)" Sortable="true" /><br /></QuickGrid><br /><Paginator State="@pagination" /><br /><br />@code {<br /> IQueryable<Student> students = Student.GetStudents();<br /> PaginationState pagination = new PaginationState { ItemsPerPage = 10 };<br /> GridSort<Student> sortByName = GridSort<Student><br /> .ByAscending(_ => _.FirstName).ThenAscending(_ => _.LastName);<br />}</span></div><h2 style="text-align: left;">Adding appropriate services and middleware to Program.cs</h2><p style="text-align: left;">Add the following <i>using</i> statement at the top of <i>Program.cs</i>:</p><p style="text-align: center;"><span style="font-family: courier;">using MvcWithBlazor.Components;</span></p><p style="text-align: left;">Add the following razor component services before the line that calls <i>builder.Build()</i>:</p><div style="text-align: left;"><span style="font-family: courier;">builder.Services<br />.AddRazorComponents()<br />.AddInteractiveServerComponents()<br />.AddCircuitOptions(options => options.DetailedErrors = true); <span style="color: #38761d;">// for debugging razor components</span></span></div><div style="text-align: left;"><br /></div><p style="text-align: left;">Add this anti-forgery middleware right after '<i>app.UseRouting();</i>':</p><p style="text-align: center;"><span style="font-family: courier;">app.UseAntiforgery();</span></p><p style="text-align: left;">Place this code before the line that calls <i>app.Run()</i>:</p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">app.MapRazorComponents<App>()</span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">.AddInteractiveServerRenderMode();</span></div></div></blockquote><div style="text-align: left;"><h2 style="text-align: left;">Try it out</h2><p>Run the application with the following terminal window command:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></p><p>When the home page loads in your browser, try the <i>/students</i> page. You should see a page that looks like this:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhxVMdoIibpTsKRNwGj8xozoxiIAZdaqva1t7EQedv-Op6xmWUjCNCzSx0qvR98T3sY6eewhL1l0KuFrwxvUKqIen0mnkKpk1XRWAGAAwwhC5soOiBYgthJS4PyNevQBsKcyPS44tXMSBn5v_Q8MIsbfPwWg1bJjgNufRxgpqix0q5fxDuOpX-YJR4MAUdA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="874" data-original-width="1424" height="392" src="https://blogger.googleusercontent.com/img/a/AVvXsEhxVMdoIibpTsKRNwGj8xozoxiIAZdaqva1t7EQedv-Op6xmWUjCNCzSx0qvR98T3sY6eewhL1l0KuFrwxvUKqIen0mnkKpk1XRWAGAAwwhC5soOiBYgthJS4PyNevQBsKcyPS44tXMSBn5v_Q8MIsbfPwWg1bJjgNufRxgpqix0q5fxDuOpX-YJR4MAUdA=w640-h392" width="640" /></a></div><br />You can sort any column by clicking on the column title and the paging should also work. We have succeeded in mixing Blazor with ASP.NET MVC. <p></p><h2 style="text-align: left;">Injecting Blazor components into MVC views</h2><p>We can inject Blazor components directly into MVC <i>.cshtml</i> views. We must first make the appropriate namespaces visible to the environment for the MVC views. Add the following <i>using</i> statements to <i>Views/_ViewImports.cshtml</i>:</p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">@using <span style="background-color: #fcff01;">MvcWithBlazor</span>.Components</span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">@using <span style="background-color: #fcff01;">MvcWithBlazor</span>.Components.Pages</span></div></div></blockquote><div style="text-align: left;"><p>Note that you must adjust the above highlighted text with the appropriate name of your application.</p><p>Add the following <i><base></i> tag and <i>Component</i> Tag Helper for a <i>HeadOutlet</i> component to the <<i>head></i> markup of <i>Views/Shared/_Layout.cshtml</i>:</p></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><base href="~/" /></span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"><component type="typeof(Microsoft.AspNetCore.Components.Web.HeadOutlet)" </span></div></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> render-mode="ServerPrerendered" /></span></div></div></blockquote><div style="text-align: left;"><p>The <i>HeadOutlet</i> component is used to render head (<i><head></i>) content for page titles (<i>PageTitle</i> component) and other head elements (<i>HeadContent</i> component) set by Razor components.</p><p>Also, in the same <i>Views/Shared/_Layout.cshtml </i>file, add a <i><script> </i>tag for the <i>blazor.web.js</i> script immediately before the <i>Scripts</i> render section (<i>@await RenderSectionAsync(...)</i>):</p><p style="text-align: center;"><span style="font-family: courier;"><script src="_framework/blazor.web.js"></script></span></p><p>Next, add the following tag to the bottom of home page <i>Views/Home/Index.cshtml</i>:</p><p style="text-align: center;"><span style="font-family: courier;"><component type="typeof(Students)" render-mode="ServerPrerendered" /></span></p><p>Point your browser to the home page of the ASP.NET Core MVC app. You should experience a page that has the <i>Students</i> component embedded inside it.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg9-GCknqJHZGnOo_bkahfCuv6tFapds9fa6I0an53hwxkAGgwxOuyARyJ8IGdb66pGYseI3IrymWXItGzPN3lv2ckQmKv5oCyk6vm1LqNWZC1iIE2MdSFROppeo-dz14xg2r91HyYGS674fkvKRYnEwlM1Uw1FxBcgGpB1FHQbAr72Rv5v87IauOIDZBVk" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1116" data-original-width="1390" height="321" src="https://blogger.googleusercontent.com/img/a/AVvXsEg9-GCknqJHZGnOo_bkahfCuv6tFapds9fa6I0an53hwxkAGgwxOuyARyJ8IGdb66pGYseI3IrymWXItGzPN3lv2ckQmKv5oCyk6vm1LqNWZC1iIE2MdSFROppeo-dz14xg2r91HyYGS674fkvKRYnEwlM1Uw1FxBcgGpB1FHQbAr72Rv5v87IauOIDZBVk=w400-h321" width="400" /></a></div>Paging and sorting work as expected.<br /><h2 style="text-align: left;">Conclusion</h2><p></p><p>You can do the same with ASP.NET Core Razor Pages.</p><p>I hope you found this article useful.</p></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-73278640745273256032023-12-06T14:10:00.000-08:002024-01-07T21:39:57.903-08:00Using free QuickGrid component with Static Server Rendering Blazor App<h2 style="text-align: left;"> Overview</h2><p>QuickGrid is a Razor component that quickly and efficiently displays data in tabular form. Visit <a href="https://aspnet.github.io/quickgridsamples/">https://aspnet.github.io/quickgridsamples/</a> to check out what you can do with this component. In this tutorial, you will discover how easy it is to incorporate in your apps and how versatile it is. I will be using the current version of .NET 8.0.</p><p>Source Code: <a href="https://github.com/medhatelmasry/BlazorQuickGrid">https://github.com/medhatelmasry/BlazorQuickGrid</a></p><p>Companion Video: <a href="https://youtu.be/LBoG1WMaFGI">https://youtu.be/LBoG1WMaFGI</a></p><h2 style="text-align: left;">Getting Started</h2><p>We will create a Static Server rendering (SSR) application with the following terminal command:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">dotnet new blazor --name BlazorQuickGrid</span> </p></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">cd BlazorQuickGrid</span></p></blockquote><p>Add this class to <i>Models/Student.cs</i> to generate some sample student data:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;"><span style="font-family: courier;">namespace </span><span style="font-family: courier;">BlazorQuickGrid.Models;</span></div></blockquote><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public class Student {</span></div><div style="text-align: left;"><span style="font-family: courier;"> required public int? Id { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"> required public string FirstName { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"> required public string LastName { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"> required public string School { get; set; }</span></div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><div style="text-align: left;"><span style="font-family: courier;"> public static IQueryable<Student> GetStudents() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> int ndx = 0;</span></div><div style="text-align: left;"><span style="font-family: courier;"> List<Student> students = new List<Student>() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Max", LastName="Pao", School="Science" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Tom", LastName="Fay", School="Mining" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Ann", LastName="Sun", School="Nursing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Joe", LastName="Fox", School="Computing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Sue", LastName="Mai", School="Mining" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Ben", LastName="Lau", School="Business" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Zoe", LastName="Ray", School="Mining" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Sam", LastName="Ash", School="Medicine" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Dan", LastName="Lee", School="Computing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Pat", LastName="Day", School="Science" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Kim", LastName="Rex", School="Computing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Tim", LastName="Ram", School="Business" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Rob", LastName="Wei", School="Mining" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Jan", LastName="Tex", School="Science" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Jim", LastName="Kid", School="Business" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Ben", LastName="Chu", School="Medicine" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Mia", LastName="Tao", School="Computing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Ted", LastName="Day", School="Business" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Amy", LastName="Roy", School="Science" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Ian", LastName="Kit", School="Nursing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Liz", LastName="Tan", School="Medicine" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Mat", LastName="Roy", School="Tourism" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Deb", LastName="Luo", School="Medicine" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Ana", LastName="Poe", School="Computing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Lyn", LastName="Raj", School="Science" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Amy", LastName="Ash", School="Tourism" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Kim", LastName="Kid", School="Mining" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Bec", LastName="Fry", School="Nursing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Eva", LastName="Lap", School="Computing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Eli", LastName="Yim", School="Business" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Sam", LastName="Hui", School="Science" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Joe", LastName="Jin", School="Mining" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Liz", LastName="Kuo", School="Agriculture" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Ric", LastName="Mak", School="Tourism" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Pam", LastName="Day", School="Computing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Stu", LastName="Gad", School="Business" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Tom", LastName="Bee", School="Tourism" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Bob", LastName="Lam", School="Agriculture" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Jim", LastName="Ots", School="Medicine" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Tom", LastName="Mag", School="Mining" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Hal", LastName="Doe", School="Agriculture" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Roy", LastName="Kim", School="Nursing" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Vis", LastName="Cox", School="Science" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Kay", LastName="Aga", School="Tourism" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Reo", LastName="Hui", School="Business" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> new Student() { Id = ++ndx, FirstName="Bob", LastName="Roe", School="Medicine" },</span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> return students.AsQueryable();</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p style="text-align: left;">Add this QuickGrid package to your server-side blazor app:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet add package Microsoft.AspNetCore.Components.QuickGrid</span></p><p>Add this to <i>Components/_Imports.razor</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">@using Microsoft.AspNetCore.Components.QuickGrid</span></div><div style="text-align: left;"><span style="font-family: courier;">@using BlazorQuickGrid.Models</span></div></blockquote><p>Replace <i>Components/Pages/Home.razor</i> with the following:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">@page "/"</span></div><div style="text-align: left;"><span style="font-family: courier;">@rendermode InteractiveServer</span></div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><div style="text-align: left;"><span style="font-family: courier;"><PageTitle>Students</PageTitle></span></div><div style="text-align: left;"><span style="font-family: courier;"><h1>Students</h1></span></div><div style="text-align: left;"><span style="font-family: courier;"><QuickGrid Items="@students"></span></div><div style="text-align: left;"><span style="font-family: courier;"> <PropertyColumn Property="@(_ => _.Id)" Sortable="true" /></span></div><div style="text-align: left;"><span style="font-family: courier;"> <PropertyColumn Property="@(_ => _.FirstName)" Sortable="true" /></span></div><div style="text-align: left;"><span style="font-family: courier;"> <PropertyColumn Property="@(_ => _.LastName)" Sortable="true" /></span></div><div style="text-align: left;"><span style="font-family: courier;"> <PropertyColumn Property="@(_ => _.School)" Sortable="true" /></span></div><div style="text-align: left;"><span style="font-family: courier;"></QuickGrid></span></div><div style="text-align: left;"><span style="font-family: courier;">@code {</span></div><div style="text-align: left;"><span style="font-family: courier;"> IQueryable<Student> students = Student.GetStudents();</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><p style="text-align: left;">You can now run your app with:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></p><p style="text-align: left;">The following page would load in your browser:</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh6OPVmKXhXUxvVvqLOrPOqVnrVUI3VZ9T1L-9fnlcSTK54zErGKNR_8IPPGDe5jbLaSmBwjY3eQErT6lbrP9HeNTGDF2oK8hgnQK3lYgtn8UrggC158w66FxgqnP3582NAGqVUWxPJpqGLsFkFtXYZnedj1ZmXRlb9rWb0sEPIW1PRBpMrRvfRccwpGcUp" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1168" data-original-width="1538" height="304" src="https://blogger.googleusercontent.com/img/a/AVvXsEh6OPVmKXhXUxvVvqLOrPOqVnrVUI3VZ9T1L-9fnlcSTK54zErGKNR_8IPPGDe5jbLaSmBwjY3eQErT6lbrP9HeNTGDF2oK8hgnQK3lYgtn8UrggC158w66FxgqnP3582NAGqVUWxPJpqGLsFkFtXYZnedj1ZmXRlb9rWb0sEPIW1PRBpMrRvfRccwpGcUp=w400-h304" width="400" /></a></div><br />One of the quick wins that you get out of the box is the ability to sort. Click on the column titles and you will see that the data in the column sorts.<h2 style="text-align: left;">Pagination</h2><div><div>Add this variable inside the <i>@code { . . . }</i> block in <i>Components/Pages/Home.razor.</i></div><div><i><br /></i></div><div style="text-align: center;"><span style="font-family: courier;">private PaginationState pagination = new PaginationState { ItemsPerPage = 10 };</span></div><div><br /></div><div>Add this parameter in to the opening <i><QuickGrid></i> tag:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">Pagination="@pagination"</span></div><div><br /></div><div>To provide for paging, add this pagination component under <i></QuickGrid></i>:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;"><Paginator State="@pagination" /></span></div><div><br /></div><div>Pagination will appear at the bottom of the table:</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjSPvCVSfa7WQMbwW5lPajUKGc_WyCvn0pIlQ1OmRCy3Q_oJtlUS7Nm-ZXeaY4NbOySq-0gfvP57VW_HZxnzG462R5TLjprPOSJNyYoNHQe3gAegeE2bnNbAstc_LW5on2ZPj8q7EDeJVQ8IB8wLQxJUgvPjBk3ee4Upytfr2sa5lr0qKL54vyDrLO96UCj" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1090" data-original-width="1864" height="234" src="https://blogger.googleusercontent.com/img/a/AVvXsEjSPvCVSfa7WQMbwW5lPajUKGc_WyCvn0pIlQ1OmRCy3Q_oJtlUS7Nm-ZXeaY4NbOySq-0gfvP57VW_HZxnzG462R5TLjprPOSJNyYoNHQe3gAegeE2bnNbAstc_LW5on2ZPj8q7EDeJVQ8IB8wLQxJUgvPjBk3ee4Upytfr2sa5lr0qKL54vyDrLO96UCj=w400-h234" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h2 style="clear: both; text-align: left;">TemplateColumn</h2><div class="separator" style="clear: both; text-align: left;">Suppose we want to combine first and last names into the same column. This is a good example of why you would use TemplateColumn. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><h4 style="clear: both; text-align: left;">Replace:</h4></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><PropertyColumn Property="@(_ => _.FirstName)" Sortable="true" /></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><PropertyColumn Property="@(_ => _.LastName)" Sortable="true" /></span></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><h4 style="clear: both; text-align: left;">WITH</h4></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><TemplateColumn Title="Name"></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <div class="flex items-center"></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> <nobr></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> @context.FirstName @context.LastName</span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </nobr></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> </div></span></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"></TemplateColumn></span></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgkev9-n6nrkLpI0l0YFDyD7ekCxIrtxF-7wLSKxgtQlKeoWW4XMNQCxnx_xYWXIxnfTpc3lwM73cYJFF4ObSrqVWkNCS_4p2enAntuxhjrWwNFFpPkF5sGVc23Y06Vc75zYJZ3_LJDT92SoT_2p-sjfcKLPqK6G7B1N3lFijrLAcBvw0DCiVvtuOsokIB-" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="968" data-original-width="1106" height="350" src="https://blogger.googleusercontent.com/img/a/AVvXsEgkev9-n6nrkLpI0l0YFDyD7ekCxIrtxF-7wLSKxgtQlKeoWW4XMNQCxnx_xYWXIxnfTpc3lwM73cYJFF4ObSrqVWkNCS_4p2enAntuxhjrWwNFFpPkF5sGVc23Y06Vc75zYJZ3_LJDT92SoT_2p-sjfcKLPqK6G7B1N3lFijrLAcBvw0DCiVvtuOsokIB-=w400-h350" width="400" /></a></div></div></div><div><br /></div></div></div><div>Although we succeeded in putting the full name under one column, unfortunately, the <i>Name</i> column is not sortable. Let us fix that.Add this <i>sortByName</i> variable into the<i> @code { . . . }</i> code block:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><span style="font-family: courier;">GridSort<Student> sortByName = GridSort<Student></span></div><div><span style="font-family: courier;"> .ByAscending(_ => _.FirstName).ThenAscending(_ => _.LastName);</span></div></blockquote><div><br /></div><div><div>Also, add this parameter to the opening <i><TemplateColumn></i> tag for <i>Name</i>:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">SortBy="@sortByName"</span></div></div><div>It is now possible to sort the Name column.</div><div><br /></div><div>There is much more you can do with the <i>QuickGrid</i> component. Consider this a small taste of what is possible.</div><div><br /></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-28711784255627278072023-11-28T13:09:00.000-08:002023-11-28T13:11:30.524-08:00Using Mongo DB in an ASP.NET 8.0 Minimal Web API web app<p> In this tutorial I will show you how to develop an ASP.NET 8.0 Minimal API application that interacts with MongoDB. In order to proceed you will need the following pre-requisites:</p><ul><li>Docker - used to host MongoDB. This is my preferred approach because it is quick and does not take too many resources on the host computer.</li><li>.NET 8.0</li><li>Visual Studio Code</li></ul><h2>The Database</h2><div>Run the following command in a terminal window to start a MongoDB container named <i>mdb</i>:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">docker run -p 27777:27017 --name mgo -d mongo:4.1.6</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>You can verify that the container is running by typing the following command in a terminal window:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">docker ps -a</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>Let us start a bash session inside the container so that we can create a database and add some sample data to it. Enter the following command to start an interactive bash session inside the container:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">docker exec -it mgo bash</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>We can then use the <i>mongo</i> command line interface (CLI) to create a database named <i>school-db</i> and then add some sample data to a collection of students. Type the following command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">mongo</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>This takes you into a <i>mongo</i> session command prompt. Enter the following command to create a database named <i>school-db</i>:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">use school-db</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>Next, let's create a collection named <i>students</i> and add to it six sample students:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: "courier new" , "courier" , monospace;">db.Students.insertMany(</span></div><div><span style="font-family: "courier new" , "courier" , monospace;">[</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"> {'FirstName':'Sue','LastName':'Fox','School':'Business'},</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"> {'FirstName':'Tom','LastName':'Max','School':'Mining'},</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"> {'FirstName':'Ann','LastName':'Lee','School':'Nursing'},</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"> {'FirstName':'Joe','LastName':'Roy','School':'Tourism'},</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"> {'FirstName':'Jan','LastName':'Ash','School':'Communications'},</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"> {'FirstName':'Eva','LastName':'Day','School':'Medicine'},</span></div><div><span style="font-family: "courier new" , "courier" , monospace;">]);</span></div></blockquote><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>You can retrieve the list of students with the following command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">db.Students.find().pretty();</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>To exit the <i>mongo</i> command prompt by typing:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">exit</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>You can also exit the bash session and return to the host operating system by typing:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">exit</span></div><h2>Creating an ASP.NET 8.0 Minimal Web API application</h2><div>Create an ASP.NET 8.0 Minimal Web API application. This is accomplished by entering this command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">dotnet new webapi -f net8.0 -o AspMongoApi</span></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>Let's see what our application looks like. Run the application by executing:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: "courier new" , "courier" , monospace;">cd </span><span style="font-family: "courier new", courier, monospace;">AspMongoApi</span></div><div><span style="font-family: "courier new" , "courier" , monospace;">dotnet run</span></div></blockquote><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>The above command builds and runs the application in a web server. Point your browser to <i>https://localhost:????/weatherforecast </i>(Where ???? is your port number). This is what you should see:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgWEw-4jRM-bWnsYLC9jBvJZUHnj1Oo435BSwFzZflTwUP8nZtQLuIZcAk8fSPwbEKxXIr7KR6D2MPwoUu9PeiuMDffpQpt4-IUpMmhwIU4qoHyGM9r4knKUsLmTDvpq4xRIe3-KRAl0NUPuy9HY76uv3N0NHE4jcr2b9m7PmoXFnhWtMb84XW9lzlQfw=s1200" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="179" data-original-width="1200" height="96" src="https://blogger.googleusercontent.com/img/a/AVvXsEgWEw-4jRM-bWnsYLC9jBvJZUHnj1Oo435BSwFzZflTwUP8nZtQLuIZcAk8fSPwbEKxXIr7KR6D2MPwoUu9PeiuMDffpQpt4-IUpMmhwIU4qoHyGM9r4knKUsLmTDvpq4xRIe3-KRAl0NUPuy9HY76uv3N0NHE4jcr2b9m7PmoXFnhWtMb84XW9lzlQfw=w640-h96" width="640" /></a></div></div><div><br /></div><div>Stop the web server by hitting <i>CTRL+C</i> on the keyboard.</div><h2>Building the Students API</h2><div>Add the MongoDB driver Nuget package with the following command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">dotnet add package MongoDB.Driver</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>Now that we have the driver, we can proceed with coding our Minimal API. I will use Visual Studio Code because it is operating system agnostic. To load your application workspace into VS Code, simply type in the following command from the root folder of your application:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">code .</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>We need a <i>Student</i> class to represent the schema for student documents in a <i>Students</i> collection in the MongoDB <i>school-db</i> database. Create a <i>Models</i> folder. Inside the <i>Models</i> folder, add a file named <i>Student.cs</i> with the following code:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier new, courier, monospace;">using MongoDB.Bson;</span></div><div><span style="font-family: courier new, courier, monospace;">using MongoDB.Bson.Serialization.Attributes;</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;">namespace AspMongoApi.Models;</span></div><div><span style="font-family: courier new, courier, monospace;">public class Student {</span></div><div><span style="font-family: courier new, courier, monospace;"> [BsonId]</span></div><div><span style="font-family: courier new, courier, monospace;"> [BsonRepresentation(BsonType.ObjectId)]</span></div><div><span style="font-family: courier new, courier, monospace;"> public string? Id { get; set; }</span></div><div><span style="font-family: courier new, courier, monospace;"> public string? FirstName { get; set; }</span></div><div><span style="font-family: courier new, courier, monospace;"> public string? LastName { get; set; }</span></div><div><span style="font-family: courier new, courier, monospace;"> </span></div><div><span style="font-family: courier new, courier, monospace;"> [BsonElement("School")]</span></div><div><span style="font-family: courier new, courier, monospace;"> public string? Department { get; set; }</span></div><div><span style="font-family: courier new, courier, monospace;">}</span></div></blockquote><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>In the preceding class, the <i>Id</i> property:</div><div><ul><li>Is required for mapping the Common Language Runtime (CLR) object to the MongoDB collection.</li><li>Is annotated with <i>[BsonId] </i>to designate this property as the document's primary key.</li><li>Is annotated with <i>[BsonRepresentation(BsonType.ObjectId)] </i>to allow passing the parameter as type string instead of an <i>ObjectId</i> structure. Mongo handles the conversion from <i>string</i> to <i>ObjectId</i>.</li></ul></div><div>The <i>Department</i> property is annotated with the <i>[BsonElement] </i>attribute. The attribute's value of <i>School</i> represents the property name in the MongoDB collection.</div><div><br /></div><div>Add the following to <i>appsettings.json</i>:</div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier new, courier, monospace;">"StudentDbSettings": {</span></div><div><span style="font-family: courier new, courier, monospace;"><span style="white-space: pre;"> </span>"CollectionName": "Students",</span></div><div><span style="font-family: courier new, courier, monospace;"><span style="white-space: pre;"> </span>"ConnectionString": "mongodb://localhost:27777",</span></div><div><span style="font-family: courier new, courier, monospace;"><span style="white-space: pre;"> </span>"DatabaseName": "school-db"</span></div><div><span style="font-family: courier new, courier, monospace;">},</span></div></blockquote><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>The entire contents of <i>appsettings.json </i>will look like this:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier new, courier, monospace;">{</span></div><div><span style="font-family: courier new, courier, monospace;"> "StudentDbSettings": {</span></div><div><span style="font-family: courier new, courier, monospace;"> "CollectionName": "Students",</span></div><div><span style="font-family: courier new, courier, monospace;"> "ConnectionString": "mongodb://localhost:27777",</span></div><div><span style="font-family: courier new, courier, monospace;"> "DatabaseName": "school-db"</span></div><div><span style="font-family: courier new, courier, monospace;"> },</span></div><div><span style="font-family: courier new, courier, monospace;"> "Logging": {</span></div><div><span style="font-family: courier new, courier, monospace;"> "LogLevel": {</span></div><div><span style="font-family: courier new, courier, monospace;"> "Default": "Information",</span></div><div><span style="font-family: courier new, courier, monospace;"> "Microsoft.AspNetCore": "Warning"</span></div><div><span style="font-family: courier new, courier, monospace;"> }</span></div><div><span style="font-family: courier new, courier, monospace;"> },</span></div><div><span style="font-family: courier new, courier, monospace;"> "AllowedHosts": "*"</span></div><div><span style="font-family: courier new, courier, monospace;">}</span></div></blockquote><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div>Add <i>StudentDbSettings.cs</i> to the <i>Models</i> folder with this code:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: "courier new" , "courier" , monospace;">namespace AspMongoApi.Models;</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"><br /></span></div><div><span style="font-family: "courier new" , "courier" , monospace;">public class StudentsDbSettings {</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"> public string ConnectionString { get; set; } = null!;</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"> public string DatabaseName { get; set; } = null!;</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"> public string CollectionName { get; set; } = null!;</span></div><div><span style="font-family: "courier new" , "courier" , monospace;">}</span></div></blockquote><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>The above <i>StudentDbSettings</i> class is used to store the <i>appsettings.json</i> file's <i>StudentDbSettings</i> property values. The JSON and C# property names are named identically to simplify the mapping process.</div><div><br /></div><div>Add the following code to <i>Program.cs </i>before "var app = builder.Build();" :</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier;">builder.Services.Configure<StudentsDbSettings>(</span></div><div><span style="font-family: courier;"> builder.Configuration.GetSection("StudentDbSettings"));</span></div></blockquote><div><br /></div><div>In the above code, the configuration instance to which the <i>appsettings.json</i> file's <i>StudentDatabaseSettings</i> section binds is registered in the Dependency Injection (DI) container.</div><div><br /></div><div>Create a folder named <i>Services</i> and add to it a file named <i>StudentService.cs</i> with the following code:</div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier new, courier, monospace;">namespace AspMongoApi.Services;</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;">public class StudentsService {</span></div><div><span style="font-family: courier new, courier, monospace;"> private readonly IMongoCollection<Student> _studentsCollection;</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;"> public StudentsService(IOptions<StudentsDbSettings> studentsDatabaseSettings) {</span></div><div><span style="font-family: courier new, courier, monospace;"> var mongoClient = new MongoClient(</span></div><div><span style="font-family: courier new, courier, monospace;"> studentsDatabaseSettings.Value.ConnectionString);</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;"> var mongoDatabase = mongoClient.GetDatabase(</span></div><div><span style="font-family: courier new, courier, monospace;"> studentsDatabaseSettings.Value.DatabaseName);</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;"> _studentsCollection = mongoDatabase.GetCollection<Student>(</span></div><div><span style="font-family: courier new, courier, monospace;"> studentsDatabaseSettings.Value.CollectionName);</span></div><div><span style="font-family: courier new, courier, monospace;"> }</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;"> public async Task<List<Student>> GetAsync() =></span></div><div><span style="font-family: courier new, courier, monospace;"> await _studentsCollection.Find(_ => true).ToListAsync();</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;"> public async Task<Student?> GetAsync(string id) =></span></div><div><span style="font-family: courier new, courier, monospace;"> await _studentsCollection.Find(x => x.Id == id).FirstOrDefaultAsync();</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;"> public async Task CreateAsync(Student newStudent) =></span></div><div><span style="font-family: courier new, courier, monospace;"> await _studentsCollection.InsertOneAsync(newStudent);</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;"> public async Task UpdateAsync(string id, Student updatedStudent) =></span></div><div><span style="font-family: courier new, courier, monospace;"> await _studentsCollection.ReplaceOneAsync(x => x.Id == id, updatedStudent);</span></div><div><span style="font-family: courier new, courier, monospace;"><br /></span></div><div><span style="font-family: courier new, courier, monospace;"> public async Task RemoveAsync(string id) =></span></div><div><span style="font-family: courier new, courier, monospace;"> await _studentsCollection.DeleteOneAsync(x => x.Id == id);</span></div><div><span style="font-family: courier new, courier, monospace;">}</span></div></blockquote><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>In the above code, an <i>StudentsDbSettings</i> instance is retrieved from Dependency Injection via constructor injection. Also, the above class knows how to connect to the MongoDB server and use the available driver methods to retrieve, insert, update and delete data.</div><div><br /></div><div>Register the <i>StudentService</i> class with DI to support constructor injection. Add the following to <i>Program.cs before "var app = builder.Build();" :</i></div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">builder.Services.AddSingleton<StudentsService>();</span></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>Add the following code to <i>Program.cs</i> right before the "app.Run()l" statement:</div><div><br /></div><div><div><span style="font-family: courier;">app.MapGet("/students", async (StudentsService studentsService) => {</span></div><div><span style="font-family: courier;"> var students = await studentsService.GetAsync();</span></div><div><span style="font-family: courier;"> return students;</span></div><div><span style="font-family: courier;">});</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">app.MapGet("/students/{id}", async (StudentsService studentsService, string id) => {</span></div><div><span style="font-family: courier;"> var student = await studentsService.GetAsync(id);</span></div><div><span style="font-family: courier;"> return student is null ? Results.NotFound() : Results.Ok(student);</span></div><div><span style="font-family: courier;">});</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">app.MapPost("/students", async (StudentsService studentsService, Student student) => {</span></div><div><span style="font-family: courier;"> await studentsService.CreateAsync(student);</span></div><div><span style="font-family: courier;"> return student;</span></div><div><span style="font-family: courier;">});</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">app.MapPut("/students/{id}", async (StudentsService studentsService, string id, Student student) => {</span></div><div><span style="font-family: courier;"> await studentsService.UpdateAsync(id, student);</span></div><div><span style="font-family: courier;"> return student;</span></div><div><span style="font-family: courier;">});</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">app.MapDelete("/students/{id}", async (StudentsService studentsService, string id) => {</span></div><div><span style="font-family: courier;"> await studentsService.RemoveAsync(id);</span></div><div><span style="font-family: courier;"> return Results.Ok();</span></div><div><span style="font-family: courier;">});</span></div></div><div><br /></div>The above endpoints:<br /><ul><li>Use the <i>StudentService</i> class to perform CRUD operations.</li><li>Contains action methods to support GET, POST, PUT, and DELETE HTTP requests.</li></ul><h2>Running the application</h2><div>Start the application by executing the this command from a terminal windows at the root of the application:</div><div><br /></div><div style="text-align: center;"><span style="font-family: "courier new" , "courier" , monospace;">dotnet watch</span></div><div><span style="font-family: "courier new" , "courier" , monospace;"></span><br /></div><div>Test the API with the Swagger interface that is available in your browser:</div><div><div class="separator" style="clear: both; text-align: center;"><br /></div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7x9sAvJX4Fh6tsqufF6WTR1tG8Ti9npP8G_AfCJN-WoTbA1JELVL8GX5oMB4ICPnV6_NrBYIs9g-SxM8km5w4ksuRodyBiq42UxLBqbszDr3xDF9jGpJ8kcV7u9Qat2J64jELcMgRR64Ph5zBkZPB8tHW8wGByXdgWkxk-2CfVqGWCHh5Xxf-9g7fQgNx/s758/Untitled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="704" data-original-width="758" height="371" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7x9sAvJX4Fh6tsqufF6WTR1tG8Ti9npP8G_AfCJN-WoTbA1JELVL8GX5oMB4ICPnV6_NrBYIs9g-SxM8km5w4ksuRodyBiq42UxLBqbszDr3xDF9jGpJ8kcV7u9Qat2J64jELcMgRR64Ph5zBkZPB8tHW8wGByXdgWkxk-2CfVqGWCHh5Xxf-9g7fQgNx/w400-h371/Untitled.png" width="400" /></a></div><br /><div><br /></div><div>You can also view the data by pointing your browser to https://localhost:????/api/Students. This is the expected output:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhWF_AZAADYXP7pfmJubQVuu1YXZ76lKIWw-DgSxGCkpv_IewGk5kYjzToN5Kup8A2_ERL93XZCbRcgqUC7zhQlQOhfPDVU-19YoV85TNX7ysdgPcLx9xG8_VbQBRvB5H1SjkISPH3nzXuhpjVqfFeAxE0ccQ6kqqr1t5mP6BIz4s2Ng4vvvZFlpfPZgA=s1182" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="206" data-original-width="1182" height="112" src="https://blogger.googleusercontent.com/img/a/AVvXsEhWF_AZAADYXP7pfmJubQVuu1YXZ76lKIWw-DgSxGCkpv_IewGk5kYjzToN5Kup8A2_ERL93XZCbRcgqUC7zhQlQOhfPDVU-19YoV85TNX7ysdgPcLx9xG8_VbQBRvB5H1SjkISPH3nzXuhpjVqfFeAxE0ccQ6kqqr1t5mP6BIz4s2Ng4vvvZFlpfPZgA=w640-h112" width="640" /></a></div><div><br /></div>Note that even though the last field is named <i>School</i> in the database, it appears as <i>Department</i> in our app because that is how we mapped it in the <i>Student</i> class.<br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div>Go ahead and test the other API endpoints for POST, PUT and DELETE using your favourite tool like Postman or curl. It should all work.</div><h2>Cleanup</h2><div>To stop & remove the MongoDB docker container, enter the following from any terminal window on your computer:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">docker rm -f mgo</span></div><div><br /></div><div>I hope you learned something new in this tutorial. Until next time, happy coding.</div><div><br /></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-4994264614054035342023-11-06T13:01:00.006-08:002023-11-07T09:10:31.678-08:00Server-side Blazor 7.0 APP with CRUD Operations and SQLiteIn this post, we will build a Server-side Blazor app talks directly to the SQLite database. This is a very realistic option since both blazor and the database server run on the server. <div><br /></div><div>Source Code: <a href="https://github.com/medhatelmasry/ServerBlazorEF7">https://github.com/medhatelmasry/ServerBlazorEF7</a><br /><h2 style="text-align: left;">Overview</h2><div>Blazor is a framework for developing interactive client-side web applications using C# instead of JavaScript. </div><h2 style="text-align: left;">Architecture</h2><div>The architecture of the application that we will be building will look like this: </div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhCWs1T7qwxM9t8JmZVSkHRc2F6vz2QmuVxZjKUH5Gue2bue70-J3aFMM0Lak8fvC4k-SKNd3CB_lggjSm0GVIwnqNaI_0lMf9CKYzBw-QJR5yoAL6aAQfP3UX1M-eJOtpjR78kxAFja8crosyfJQhiK5SjaZkBmVq2LfNmCuCCIy4fNeMM1cqxlktVFjqc" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="440" data-original-width="1266" height="222" src="https://blogger.googleusercontent.com/img/a/AVvXsEhCWs1T7qwxM9t8JmZVSkHRc2F6vz2QmuVxZjKUH5Gue2bue70-J3aFMM0Lak8fvC4k-SKNd3CB_lggjSm0GVIwnqNaI_0lMf9CKYzBw-QJR5yoAL6aAQfP3UX1M-eJOtpjR78kxAFja8crosyfJQhiK5SjaZkBmVq2LfNmCuCCIy4fNeMM1cqxlktVFjqc=w640-h222" width="640" /></a></div><br /><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div>ASP.NET Core hosts the server-side app and sets up SignalR endpoint where clients connect. SignalR is responsible for updating the DOM on the client with any changes. The Blazor application on the server connects directly to the database using Entity Framework Core.</div><h2 style="text-align: left;">What are we doing in this tutorial?</h2><div>In this tutorial I will show you how to build a server-side Blazor application that connects directly with SQLite database using Entity Framework Core.</div><h2 style="text-align: left;">Let's start coding</h2><div>1) In a terminal window, go to your working directory. Enter the following command to create a Server-Side Blazor application inside a directory called <i>ServerBlazorEF</i>: </div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet new blazorserver -f net7.0 -o ServerBlazorEF</span></div><div><br /></div><div>2) Open the <i>ServerBlazorEF</i> folder in Visual Studio Code.</div><div><br /></div><div>3) For a Blazor server-side project, the IDE requests that you add assets to build and debug the project. Select <i>Yes</i>.</div><div><br /></div><div>4) Hit <i>Ctrl F5 (</i>or <i>dotnet watch </i>in a terminal window<i>) </i>to run the application. Your default browser will load a page that looks like this: </div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgK-lzoMtTsPmi00uhzdvllVWLMWT1WfrpoqlUchZkAyg6VscY-5MJstJ7rrWEonJF5urJpF86p01nL--d_d6ghp_fxA3a5k18nn5RzReb_EmM_O51hUoJVsFMDcgnSUu0vrFKn_FwA7Ro5s4mU4puaw_pbDckR-QuxBTFIcRt_rL8fEyLgZOWgUYXhjVC7" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="322" data-original-width="683" height="302" src="https://blogger.googleusercontent.com/img/a/AVvXsEgK-lzoMtTsPmi00uhzdvllVWLMWT1WfrpoqlUchZkAyg6VscY-5MJstJ7rrWEonJF5urJpF86p01nL--d_d6ghp_fxA3a5k18nn5RzReb_EmM_O51hUoJVsFMDcgnSUu0vrFKn_FwA7Ro5s4mU4puaw_pbDckR-QuxBTFIcRt_rL8fEyLgZOWgUYXhjVC7=w640-h302" width="640" /></a></div><br /><div>Our objective is to extend the above application so that it talks to SQLite using Entity Framework Core. To this end, we will be dealing with a very simple student model. Therefore, add a <i>Student.cs</i> class file in a folder named <i>Models </i>with the following content: </div><div><br /></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><div><span style="font-family: courier;">public class Student {</span></div></div></div><div><div><div><span style="font-family: courier;"> public int StudentId { get; set; }</span></div></div></div><div><div><div><span style="font-family: courier;"> [Required]</span></div></div></div><div><div><div><span style="font-family: courier;"> public string? FirstName { get; set; }</span></div></div></div><div><div><div><span style="font-family: courier;"> [Required]</span></div></div></div><div><div><div><span style="font-family: courier;"> public string? LastName { get; set; }</span></div></div></div><div><div><div><span style="font-family: courier;"> [Required]</span></div></div></div><div><div><div><span style="font-family: courier;"> public string? School { get; set; }</span></div></div></div><div><div><div><span style="font-family: courier;">}</span></div></div></div></blockquote><div><div><div><br /></div><div>Since we will be using SQLite, we will need to add the appropriate packages. Therefore, from within a terminal window at the root of your <i>ServerBlazorEF </i>project, run the following commands that will add the appropriate database related packages: </div><div><br /></div></div></div><span style="font-family: courier;">dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore</span><br /><span style="font-family: courier;">dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore</span><br /><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore.Sqlite</span><br /><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore.Design</span><br /><div style="text-align: left;"><div><div><span style="font-family: courier;">dotnet add package CsvHelper </span></div></div></div><div><div><div><br /></div><div>We need to add a connection string for the database. Add the following to the <i>appsettings.json</i> file: </div><div><br /></div><div><div><span style="font-family: courier;">"ConnectionStrings": {</span></div><div><span style="font-family: courier;"> "DefaultConnection": "DataSource=college.db;Cache=Shared"</span></div><div><span style="font-family: courier;">}</span></div></div><div> </div><div>We will be using the Entity Framework Code First approach. </div><div><br /></div><div>Developers prefer having sample data when building data driven applications. Therefore, we will create some sample data to ensure that our application behaves as expected. Copy the following data and save it in a text file named <i>students.csv</i> in the <i>wwwroot </i>folder:</div><div><br /></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><div><span style="font-family: courier;">StudentId,FirstName,LastName,School</span></div></div></div><div><div><div><span style="font-family: courier;">1,Tom,Max,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">2,Ann,Fay,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">3,Joe,Sun,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">4,Sue,Fox,Computing</span></div></div></div><div><div><div><span style="font-family: courier;">5,Ben,Ray,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">6,Zoe,Cox,Business</span></div></div></div><div><div><div><span style="font-family: courier;">7,Sam,Ray,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">8,Dan,Ash,Medicine</span></div></div></div><div><div><div><span style="font-family: courier;">9,Pat,Lee,Computing</span></div></div></div><div><div><div><span style="font-family: courier;">10,Kim,Day,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">11,Tim,Rex,Computing</span></div></div></div><div><div><div><span style="font-family: courier;">12,Rob,Ram,Business</span></div></div></div><div><div><div><span style="font-family: courier;">13,Jan,Fry,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">14,Jim,Tex,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">15,Ben,Kid,Business</span></div></div></div><div><div><div><span style="font-family: courier;">16,Mia,Chu,Medicine</span></div></div></div><div><div><div><span style="font-family: courier;">17,Ted,Tao,Computing</span></div></div></div><div><div><div><span style="font-family: courier;">18,Amy,Day,Business</span></div></div></div><div><div><div><span style="font-family: courier;">19,Ian,Roy,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">20,Liz,Kit,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">21,Mat,Tan,Medicine</span></div></div></div><div><div><div><span style="font-family: courier;">22,Deb,Roy,Medicine</span></div></div></div><div><div><div><span style="font-family: courier;">23,Ana,Ray,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">24,Lyn,Poe,Computing</span></div></div></div><div><div><div><span style="font-family: courier;">25,Amy,Raj,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">26,Kim,Ash,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">27,Bec,Kid,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">28,Eva,Fry,Computing</span></div></div></div><div><div><div><span style="font-family: courier;">29,Eli,Lap,Business</span></div></div></div><div><div><div><span style="font-family: courier;">30,Sam,Yim,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">31,Joe,Hui,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">32,Liz,Jin,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">33,Ric,Kuo,Business</span></div></div></div><div><div><div><span style="font-family: courier;">34,Pam,Mak,Computing</span></div></div></div><div><div><div><span style="font-family: courier;">35,Cat,Yao,Medicine</span></div></div></div><div><div><div><span style="font-family: courier;">36,Lou,Zhu,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">37,Tom,Dag,Business</span></div></div></div><div><div><div><span style="font-family: courier;">38,Stu,Day,Business</span></div></div></div><div><div><div><span style="font-family: courier;">39,Tom,Gad,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">40,Bob,Bee,Business</span></div></div></div><div><div><div><span style="font-family: courier;">41,Jim,Ots,Business</span></div></div></div><div><div><div><span style="font-family: courier;">42,Tom,Mag,Business</span></div></div></div><div><div><div><span style="font-family: courier;">43,Hal,Doe,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">44,Roy,Kim,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">45,Vis,Cox,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">46,Kay,Aga,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">47,Reo,Hui,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">48,Bob,Roe,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">49,Jay,Eff,Computing</span></div></div></div><div><div><div><span style="font-family: courier;">50,Eva,Chu,Business</span></div></div></div><div><div><div><span style="font-family: courier;">51,Lex,Rae,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">52,Lin,Dex,Mining</span></div></div></div><div><div><div><span style="font-family: courier;">53,Tom,Dag,Business</span></div></div></div><div><div><div><span style="font-family: courier;">54,Ben,Shy,Computing</span></div></div></div><div><div><div><span style="font-family: courier;">55,Rob,Bos,Nursing</span></div></div></div><div><div><div><span style="font-family: courier;">56,Ali,Mac,Business</span></div></div></div><div><div><div><span style="font-family: courier;">57,Ken,Sim,Medicine</span></div></div></div></blockquote><div><div><div><br /></div><div>The starting point is to create a database context class. Add a C# class file named <i>SchoolDbContext.cs</i> in the <i>Data </i>folder with the following class code: </div><div><br /></div><div><span style="font-family: courier;">public class SchoolDbContext : DbContext {</span></div><div><span style="font-family: courier;"> public DbSet<Student> Students => Set<Student>();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public SchoolDbContext(DbContextOptions<SchoolDbContext> options)</span></div><div><span style="font-family: courier;"> : base(options) { }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> protected override void OnModelCreating(ModelBuilder modelBuilder) {</span></div><div><span style="font-family: courier;"> base.OnModelCreating(modelBuilder);</span></div><div><span style="font-family: courier;"> modelBuilder.Entity<Student>().HasData(GetStudents());</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> </span></div><div><span style="font-family: courier;"> private static IEnumerable<Student> GetStudents() {</span></div><div><span style="font-family: courier;"> string[] p = { Directory.GetCurrentDirectory(), "wwwroot", "students.csv" };</span></div><div><span style="font-family: courier;"> var csvFilePath = Path.Combine(p);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var config = new CsvConfiguration(CultureInfo.InvariantCulture) {</span></div><div><span style="font-family: courier;"> PrepareHeaderForMatch = args => args.Header.ToLower(),</span></div><div><span style="font-family: courier;"> };</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var data = new List<Student>().AsEnumerable();</span></div><div><span style="font-family: courier;"> using (var reader = new StreamReader(csvFilePath)) {</span></div><div><span style="font-family: courier;"> using (var csvReader = new CsvReader(reader, config)) {</span></div><div><span style="font-family: courier;"> data = csvReader.GetRecords<Student>().ToList();</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> return data;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div><div><br /></div><div>Notice the above code is adding the contents of the <i>wwwroot/students.csv</i> file as seed data into the database.</div><div><br /></div><div>In the <i>Program.cs </i>file, just before ‘<i>var app = builder.Build();</i>’, add the following code so that our application can use SQLite:</div><div><br /></div><div><div><span style="font-family: courier;">var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");</span></div><div><span style="font-family: courier;">builder.Services.AddDbContext<SchoolDbContext>(</span></div><div><span style="font-family: courier;"> options => options.UseSqlite(connectionString)</span></div><div><span style="font-family: courier;">);</span></div></div><div> </div><div>We are now ready to apply Entity Framework migrations, create the database and seed some sample data. If you have not done so already, you will need to globally install the Entity Framework CLI tool. This tooling is installed globally on your computer by running the following command in a terminal window:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet tool install --global dotnet-ef</span></div><div><br /></div><div>Remember to build your entire solution before proceeding. Then, from within a terminal window inside the <i>ServerBlazorEF </i>root directory, run the following command to create migrations: </div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet ef migrations add M1 -o Data/Migrations</span></div><div><br /></div><div> You should get no errors and this results in the creation of a migration file ending with the name ....M1.cs in the Migrations folder which contains commands for inserting sample data.</div><div><br /></div><div>The next step is to create the SQLite <i>college.db</i> database file. This is done by running the following command from inside a terminal window at the root folder of the application. </div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet ef database update</span></div><div><br /></div><div>If no errors are encountered, you can assume that the database was created and properly seeded with data.</div><div><br /></div><div>Add a class file named <i>StudentService.cs </i>in the <i>Data </i>folder with following code: </div><div><br /></div><div><div><span style="font-family: courier;">public class StudentService {</span></div><div><span style="font-family: courier;"> private SchoolDbContext _context;</span></div><div><span style="font-family: courier;"> </span></div><div><span style="font-family: courier;"> public StudentService(SchoolDbContext context) {</span></div><div><span style="font-family: courier;"> _context = context;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public async Task<List<Student>> GetStudentsAsync() {</span></div><div><span style="font-family: courier;"> return await _context.Students.ToListAsync();</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public async Task<Student?> GetStudentByIdAsync(int id) {</span></div><div><span style="font-family: courier;"> return await _context.Students.FindAsync(id) ?? null;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public async Task<Student?> InsertStudentAsync(Student student) {</span></div><div><span style="font-family: courier;"> _context.Students.Add(student);</span></div><div><span style="font-family: courier;"> await _context!.SaveChangesAsync();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> return student;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public async Task<Student> UpdateStudentAsync(int id, Student s) {</span></div><div><span style="font-family: courier;"> var student = await _context.Students!.FindAsync(id);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (student == null)</span></div><div><span style="font-family: courier;"> return null!;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> student.FirstName = s.FirstName;</span></div><div><span style="font-family: courier;"> student.LastName = s.LastName;</span></div><div><span style="font-family: courier;"> student.School = s.School;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> _context.Students.Update(student);</span></div><div><span style="font-family: courier;"> await _context.SaveChangesAsync();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> return student!;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public async Task<Student> DeleteStudentAsync(int id) {</span></div><div><span style="font-family: courier;"> var student = await _context.Students!.FindAsync(id);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (student == null)</span></div><div><span style="font-family: courier;"> return null!;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> _context.Students.Remove(student);</span></div><div><span style="font-family: courier;"> await _context.SaveChangesAsync();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> return student!;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> private bool StudentExists(int id) {</span></div><div><span style="font-family: courier;"> return _context.Students!.Any(e => e.StudentId == id);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div></div><div><br /></div><div>The above <i>StudentService</i> class provides all the necessary methods for CRUD operations involving data retrieval, insertion, update and deletion.</div><div><br /></div><div>We need to configure the <i>StudentService </i>class as a scoped service so that we can use dependency injection. Scoped lifetime services are created once per client request (connection). Add the following statement to the Program.cs just before ‘var app = builder.Build()’: </div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">builder.Services.AddScoped<StudentService>();</span></div><div><br /></div><div>Close all the files in your editor. Rename <i>FetchData.razor </i>file in the <i>Pages </i>folder to <i>Students.razor</i>. Replace its contents with the following code: </div><div><br /></div><div><span style="font-family: courier;">@page "/students"</span></div><div><span style="font-family: courier;">@using ServerBlazorEF.Data</span></div><div><span style="font-family: courier;">@using ServerBlazorEF.Models</span></div><div><span style="font-family: courier;">@inject StudentService studentService</span></div><div><span style="font-family: courier;"><h1>Students</h1></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">@if (students == null) {</span></div><div><span style="font-family: courier;"> <p><em>Loading...</em></p></span></div><div><span style="font-family: courier;">} else {</span></div><div><span style="font-family: courier;"> <table class='table table-hover'></span></div><div><span style="font-family: courier;"> <thead></span></div><div><span style="font-family: courier;"> <tr></span></div><div><span style="font-family: courier;"> <th>ID</th></span></div><div><span style="font-family: courier;"> <th>First Name</th></span></div><div><span style="font-family: courier;"> <th>Last Name</th></span></div><div><span style="font-family: courier;"> <th>School</th></span></div><div><span style="font-family: courier;"> </tr></span></div><div><span style="font-family: courier;"> </thead></span></div><div><span style="font-family: courier;"> <tbody></span></div><div><span style="font-family: courier;"> @foreach (var item in students)</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> <tr></span></div><div><span style="font-family: courier;"> <td>@item.StudentId</td></span></div><div><span style="font-family: courier;"> <td>@item.FirstName</td></span></div><div><span style="font-family: courier;"> <td>@item.LastName</td></span></div><div><span style="font-family: courier;"> <td>@item.School</td></span></div><div><span style="font-family: courier;"> </tr></span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> </tbody></span></div><div><span style="font-family: courier;"> </table></span></div><div><span style="font-family: courier;">}</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">@code {</span></div><div><span style="font-family: courier;"> List<Student>? students;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> protected override async Task OnInitializedAsync() {</span></div><div><span style="font-family: courier;"> </span><span style="font-family: courier;">students = await studentService.GetStudentsAsync();</span></div><div><span style="font-family: courier;"> }</span></div><div><br /></div><div><span style="font-family: courier;">}</span></div><div><span style="font-family: courier;"><br /></span></div><div>Since we will be using the <i>Student </i>class in multiple razor pages, move the <i>@using ServerBlazorEF.Models </i>statement on line 3 in the above code to <i>_Imports.razor</i>.</div><div><br /></div><div>Let us focus on the <i>@code </i>block. The <i>OnInitAsyns()</i> method is called when the page gets loaded. It makes a call to the student service which loads a list of students from the database. The remaining HTML/Razor code simply displays the data in a table.</div><div><br /></div><div>Let's modify the menu item on the left navigation of our application. Open <i>Shared/NavMenu.razor </i>in the editor and change the link for “Fetch data” so it looks like this:</div><div><br /></div><div><div><span style="font-family: courier;"><div class="nav-item px-3"></span></div><div><span style="font-family: courier;"> <NavLink class="nav-link" href="students"></span></div><div><span style="font-family: courier;"> <span class="oi oi-list-rich" aria-hidden="true"></span> Get Students</span></div><div><span style="font-family: courier;"> </NavLink></span></div><div><span style="font-family: courier;"></div></span></div></div><div><br /></div><div>You must be eager to test out the server-side Blazor project. Run your app and select the “Get Students” link on the left navigation, this is what the output will look like: </div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgzpG3a-QvssD_ioHX1yppCApy00XIB-qNlEgYKJ5sy1uccDclmDHnwhvpgEUSoLV33DQkSCSMCp0BzSPUUmWYbN7izrJAlFpFXRI_ZewHPdemlgrOBSHl22vplZKZm31bLrG2T1nVb-jgmkCHgpP4khHh3-LAKrtP0zqYguzdBaWY4uDXi_QD44OU94_qO" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="408" data-original-width="788" height="332" src="https://blogger.googleusercontent.com/img/a/AVvXsEgzpG3a-QvssD_ioHX1yppCApy00XIB-qNlEgYKJ5sy1uccDclmDHnwhvpgEUSoLV33DQkSCSMCp0BzSPUUmWYbN7izrJAlFpFXRI_ZewHPdemlgrOBSHl22vplZKZm31bLrG2T1nVb-jgmkCHgpP4khHh3-LAKrtP0zqYguzdBaWY4uDXi_QD44OU94_qO=w640-h332" width="640" /></a></div></div><div><h2 style="text-align: left;">Adding data</h2><div>Our Blazor app is not complete without add, edit and delete functionality. We shall start with adding data. </div><div><br /></div><div>Let us re-purpose <i>Counter.razor</i> so that it becomes our page for adding data. Rename <i>Counter.razor</i> to <i>AddStudent.razor</i>.</div><div><br /></div><div>Replace <i>AddStudent.razor </i>with the following code:</div><div><br /></div><div><span style="font-family: courier;">@page "/addstudent"</span></div><div><span style="font-family: courier;">@using ServerBlazorEF.Models</span></div><div><span style="font-family: courier;">@inject ServerBlazorEF.Data.StudentService </span><span style="font-family: courier;">studentService</span></div><div><span style="font-family: courier;">@inject NavigationManager NavManager</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><PageTitle>Add Student</PageTitle></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><h1>Add Student</h1></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><EditForm Model="@student" OnValidSubmit="HandleValidSubmit"></span></div><div><span style="font-family: courier;"> <DataAnnotationsValidator /></span></div><div><span style="font-family: courier;"> <ValidationSummary /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> <div class="form-group"></span></div><div><span style="font-family: courier;"> <label for="FirstName">First Name:</label></span></div><div><span style="font-family: courier;"> <InputText id="FirstName" class="form-control" @bind-Value="student.FirstName" /></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> <div class="form-group"></span></div><div><span style="font-family: courier;"> <label for="LastName">Last Name:</label></span></div><div><span style="font-family: courier;"> <InputText id="LastName" class="form-control" @bind-Value="student.LastName" /></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> <div class="form-group"></span></div><div><span style="font-family: courier;"> <label for="School">School:</label></span></div><div><span style="font-family: courier;"> <InputText id="School" class="form-control" @bind-Value="student.School" /></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> <button type="submit" class="btn btn-primary">Submit</button></span></div><div><span style="font-family: courier;"></EditForm></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">@code {</span></div><div><span style="font-family: courier;"> private Student student = new Student();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> private async Task HandleValidSubmit() {</span></div><div><span style="font-family: courier;"> await studentService.InsertStudentAsync(student);</span></div><div><span style="font-family: courier;"> NavManager.NavigateTo("/students");</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div><div><span style="font-family: courier;"><br /></span></div><div><div><span style="font-family: courier;">Open <i>Shared/NavMenu.razor</i> in the editor and change the link for “Counter” so it looks like this:</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><div class="nav-item px-3"></span></div><div><span style="font-family: courier;"> <NavLink class="nav-link" href="addstudent"></span></div><div><span style="font-family: courier;"> <span class="oi oi-list-rich" aria-hidden="true"></span> Add Student</span></div><div><span style="font-family: courier;"> </NavLink></span></div><div><span style="font-family: courier;"></div></span></div></div><div><br /></div><div>Run the Blazor server-side project and select <i>Add Student </i>on the left navigation menu. This is what it should look like: </div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEictqUePPO2WlK1aCZtlScCMKBJKXBA1HCwejkpO3VxPFmFriKaYcXczeAh-mNfzIQxr3zUrh5F9Otz1PhgJ-hg_AUojqFNg4IAAlYP7g5vro6bNBQV4MwNJYhEvWKPz6-fBAlfh46Z9WR8sR_rus6s-ZC29STMVFKUQghe_44J2MuyWWMyw2O657wnVkqx" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="376" data-original-width="540" height="279" src="https://blogger.googleusercontent.com/img/a/AVvXsEictqUePPO2WlK1aCZtlScCMKBJKXBA1HCwejkpO3VxPFmFriKaYcXczeAh-mNfzIQxr3zUrh5F9Otz1PhgJ-hg_AUojqFNg4IAAlYP7g5vro6bNBQV4MwNJYhEvWKPz6-fBAlfh46Z9WR8sR_rus6s-ZC29STMVFKUQghe_44J2MuyWWMyw2O657wnVkqx=w400-h279" width="400" /></a></div><br />I entered <i>Bob</i>, <i>Smith </i>and <i>Travel </i>for data and when I clicked on the <i>Submit </i>button I got the following data inserted into the database:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhDwjSTpPd0JelEbWDxg5C0DFulzLg8lD4n-QPi-3zNRlZIGaWFPrL2Mxyl7Cf_oynA58rM8j6XjQnUnkbswBHaN3h33ZIjkfiQieq7RIScVbAPngDh0j1RDFM5uRPN7bTo9nRjS8vegoi_vREopRzYDPzek9riJA6aykxdyQ7hGZ2w4sQiLa05hHnzJLzT" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="372" data-original-width="1208" height="198" src="https://blogger.googleusercontent.com/img/a/AVvXsEhDwjSTpPd0JelEbWDxg5C0DFulzLg8lD4n-QPi-3zNRlZIGaWFPrL2Mxyl7Cf_oynA58rM8j6XjQnUnkbswBHaN3h33ZIjkfiQieq7RIScVbAPngDh0j1RDFM5uRPN7bTo9nRjS8vegoi_vREopRzYDPzek9riJA6aykxdyQ7hGZ2w4sQiLa05hHnzJLzT=w640-h198" width="640" /></a></div><br /></div><div><h2 style="text-align: left;">Update & Delete data using PUT & DELETE methods</h2><div>We want to be able to select a row of data and update or delete it. Add the following additional cells to the table row in <i>Students.razor</i>: </div><div><br /></div><div><span style="font-family: courier;"><td><a class="btn btn-success btn-sm" href="/updel/@item.StudentId/edit">edit</a></td><br /><br /></span></div><div><span style="font-family: courier;"><td><a class="btn btn-danger btn-sm" href="/updel/@item.StudentId/del">del</a></td> </span></div><div><br /></div><div>The above would pass the appropriate <i>studentId </i>and <i>mode parameters </i>to another page with route <i>/updel</i>.</div><div><br /></div><div>Create a text file named <i>UpdateDelete.razor</i> in the <i>Pages </i>folder with the following content:</div><div><br /></div><div><span style="font-family: courier;">@page "/updel/{id}/{mode}"<br />@using ServerBlazorEF.Models</span></div><div><span style="font-family: courier;">@inject ServerBlazorEF.Data.StudentService studentService</span></div><div><span style="font-family: courier;">@inject NavigationManager NavManager</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><style></span></div><div><span style="font-family: courier;"> fieldset {</span></div><div><span style="font-family: courier;"> border: 2px solid #000;</span></div><div><span style="font-family: courier;"> padding-left: 20px;</span></div><div><span style="font-family: courier;"> margin-bottom: 20px;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"></style></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><PageTitle>Update/Delete Student</PageTitle></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">@if (student != null && Mode == "edit") // Update</span></div><div><span style="font-family: courier;">{</span></div><div><span style="font-family: courier;"> <p>Update Student with ID == @Id</p></span></div><div><span style="font-family: courier;"> <EditForm Model="@student" OnValidSubmit="HandleValidSubmit"></span></div><div><span style="font-family: courier;"> <DataAnnotationsValidator /></span></div><div><span style="font-family: courier;"> <ValidationSummary /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> <div class="form-group"></span></div><div><span style="font-family: courier;"> <label for="FirstName">First Name:</label></span></div><div><span style="font-family: courier;"> <InputText id="FirstName" class="form-control" @bind-Value="student.FirstName" /></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> <div class="form-group"></span></div><div><span style="font-family: courier;"> <label for="LastName">Last Name:</label></span></div><div><span style="font-family: courier;"> <InputText id="LastName" class="form-control" @bind-Value="student.LastName" /></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> <div class="form-group"></span></div><div><span style="font-family: courier;"> <label for="School">School:</label></span></div><div><span style="font-family: courier;"> <InputText id="School" class="form-control" @bind-Value="student.School" /></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> <button type="submit" class="btn btn-primary">Update</button></span></div><div><span style="font-family: courier;"> </EditForm></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> @code {</span></div><div><span style="font-family: courier;"> private async Task HandleValidSubmit()</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> await studentService.UpdateStudentAsync(student!.StudentId, student);</span></div><div><span style="font-family: courier;"> NavManager.NavigateTo("/students");</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div><div><span style="font-family: courier;">else if (student != null && Mode == "del")</span></div><div><span style="font-family: courier;">{ // Delete</span></div><div><span style="font-family: courier;"> // display student details</span></div><div><span style="font-family: courier;"> <fieldset></span></div><div><span style="font-family: courier;"> <legend>Student Information</legend></span></div><div><span style="font-family: courier;"> <p>Student ID: @Id</p></span></div><div><span style="font-family: courier;"> <p>First Name: @student.FirstName</p></span></div><div><span style="font-family: courier;"> <p>Last Name: @student.LastName</p></span></div><div><span style="font-family: courier;"> <p>School: @student.School</p></span></div><div><span style="font-family: courier;"> </fieldset></span></div><div><span style="font-family: courier;"> <p>Delete Student with ID == @Id</p></span></div><div><span style="font-family: courier;"> <p>Are you sure?</p></span></div><div><span style="font-family: courier;"> <button type="button" class="btn btn-danger" @onclick="HandleDeleteStudent">Delete</button></span></div><div><span style="font-family: courier;"> @code {</span></div><div><span style="font-family: courier;"> private async Task HandleDeleteStudent()</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> await studentService.DeleteStudentAsync(student!.StudentId);</span></div><div><span style="font-family: courier;"> NavManager.NavigateTo("/students");</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div><div><span style="font-family: courier;">}</span></div><div><span style="font-family: courier;">else</span></div><div><span style="font-family: courier;">{</span></div><div><span style="font-family: courier;"> <p>Student with ID == @Id not found</p></span></div><div><span style="font-family: courier;">}</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">@code {</span></div><div><span style="font-family: courier;"> [Parameter]</span></div><div><span style="font-family: courier;"> public string? Id { get; set; }</span></div><div><span style="font-family: courier;"> [Parameter]</span></div><div><span style="font-family: courier;"> public string? Mode { get; set; }</span></div><div><span style="font-family: courier;"> private Student? student = new Student();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> protected override async Task OnInitializedAsync()</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> int intId = Convert.ToInt32(Id);</span></div><div><span style="font-family: courier;"> student = await studentService.GetStudentByIdAsync(intId);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div><div><br /></div><div>Note how parameters are passed from one page to another.</div><div><br /></div><div>1) {id}/{mode} are defined in the route</div><div>2) In the @code section, the following parameters are defined:</div><div><br /></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><div><span style="font-family: courier;">[Parameter]</span></div></div></div><div><div><div><span style="font-family: courier;">public string? Id { get; set; }</span></div></div></div><div><div><div><span style="font-family: courier;">[Parameter]</span></div></div></div><div><div><div><span style="font-family: courier;">public string? Mode { get; set; }</span></div></div></div></blockquote><div><h2 style="text-align: left;">CRUD Experience</h2><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjteFsiXpF5DXbuO2UKjRTCaS5L03WKk-eP19O5J084Qc1w0y56Soy94fiWncS_rifKGSYAPTabCFRV1x7_t2vNTw6mpB7PRpOeEEcOyh7grkih2IJwAHnaGnHPHdDTNYRyaUd4okNgduO6ZDM-PTRb9IXJqRnW1x4okV7DIlRShppu6xF-HLyqOIB7HOHU" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="354" data-original-width="952" height="238" src="https://blogger.googleusercontent.com/img/a/AVvXsEjteFsiXpF5DXbuO2UKjRTCaS5L03WKk-eP19O5J084Qc1w0y56Soy94fiWncS_rifKGSYAPTabCFRV1x7_t2vNTw6mpB7PRpOeEEcOyh7grkih2IJwAHnaGnHPHdDTNYRyaUd4okNgduO6ZDM-PTRb9IXJqRnW1x4okV7DIlRShppu6xF-HLyqOIB7HOHU=w640-h238" width="640" /></a> </div><div class="separator" style="clear: both; text-align: center;"><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgI8ahuxwu8Z7hG-ULRna_VQ4rkxALcMcxzmnzExbqKxpkinhmhHAYz5FInmiUB8NxtP0sQAu5MCSpNxMM7puIycgj5oJ2OjCDGqmGshfAjV1GuLMPlmvdLas2Z_YjQiOQdiZzQIUcTn6KH5rCnN8Iyb9PSFPl3CtQ16T7ORxVw62rc-H9Z2wpK6802Wmew" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="763" data-original-width="1044" height="293" src="https://blogger.googleusercontent.com/img/a/AVvXsEgI8ahuxwu8Z7hG-ULRna_VQ4rkxALcMcxzmnzExbqKxpkinhmhHAYz5FInmiUB8NxtP0sQAu5MCSpNxMM7puIycgj5oJ2OjCDGqmGshfAjV1GuLMPlmvdLas2Z_YjQiOQdiZzQIUcTn6KH5rCnN8Iyb9PSFPl3CtQ16T7ORxVw62rc-H9Z2wpK6802Wmew=w400-h293" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjfusqnwZ3j1xwf1l_9gQlu83Reypt8NhfodLOISNqJskCpl0_VNH8F1a4ax9PrWfEqjg5xXenKebBt3H9nCh_wAYdDS9qwSi8aMcWMv5e2vOFSlmfax1XbkJ85x6gkt3Q5X_HazWvUqjvDQyoKZV6eS5Hqr-1k1tcIzavYQYnrwURowMhsbwpbmeuaQp-0" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="734" data-original-width="2130" height="220" src="https://blogger.googleusercontent.com/img/a/AVvXsEjfusqnwZ3j1xwf1l_9gQlu83Reypt8NhfodLOISNqJskCpl0_VNH8F1a4ax9PrWfEqjg5xXenKebBt3H9nCh_wAYdDS9qwSi8aMcWMv5e2vOFSlmfax1XbkJ85x6gkt3Q5X_HazWvUqjvDQyoKZV6eS5Hqr-1k1tcIzavYQYnrwURowMhsbwpbmeuaQp-0=w640-h220" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgkOjT68xT4OHSGw8EjmR_hY59qhlU2xr8PUabcBerCHm89sXKHbLvMrWdq6RnTxW_DXkJpjAFk4rhD7k5M5JbBvaREWk-nXhECgDtmWlL-2IL_eKhtt_9TXOwYpvCzKU78MwzSIOZU7VI8M8glq8jMU_Kdwuhy3J4wTQSZ7p8MliRYAeCz8ZXbbQESos74" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="746" data-original-width="1034" height="289" src="https://blogger.googleusercontent.com/img/a/AVvXsEgkOjT68xT4OHSGw8EjmR_hY59qhlU2xr8PUabcBerCHm89sXKHbLvMrWdq6RnTxW_DXkJpjAFk4rhD7k5M5JbBvaREWk-nXhECgDtmWlL-2IL_eKhtt_9TXOwYpvCzKU78MwzSIOZU7VI8M8glq8jMU_Kdwuhy3J4wTQSZ7p8MliRYAeCz8ZXbbQESos74=w400-h289" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiWuDqdnjWGpN3NvszQEyqKVX1W9WO8sbAFU6S7fYddlzMrqt6CG2rWHB0GP9LS1sY45HvabyZEBVXHef1oMgbxfIrXlb36BXxUzsIDAJTzfZ1_9FQth6_ZLzxFCWaam3sdE5Mxs6pyqyLPb94zK2bw7CkJf6CEXiM5zqN5K6hYhMwjYxi-BsGQbl0PvPTi" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="714" data-original-width="2206" height="208" src="https://blogger.googleusercontent.com/img/a/AVvXsEiWuDqdnjWGpN3NvszQEyqKVX1W9WO8sbAFU6S7fYddlzMrqt6CG2rWHB0GP9LS1sY45HvabyZEBVXHef1oMgbxfIrXlb36BXxUzsIDAJTzfZ1_9FQth6_ZLzxFCWaam3sdE5Mxs6pyqyLPb94zK2bw7CkJf6CEXiM5zqN5K6hYhMwjYxi-BsGQbl0PvPTi=w640-h208" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi-L6ltkyUNSWdOD6RhoS8IbzGqIH9CHALVOpW0O86r9PHyk2MOe6yECXwMX8kNNS7HbmxeAPCk9lnrU5iuX9hbK2_Ty6PdJvPCd7UMP39yUbCjE5f93cUzMFL4xlcVbMFSztCWDbBwbgEjl0zNgoUF1j0G6dV6suCucttnBhMTIrbPTZ_gWAjBdTCMquTB" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="662" data-original-width="2218" height="192" src="https://blogger.googleusercontent.com/img/a/AVvXsEi-L6ltkyUNSWdOD6RhoS8IbzGqIH9CHALVOpW0O86r9PHyk2MOe6yECXwMX8kNNS7HbmxeAPCk9lnrU5iuX9hbK2_Ty6PdJvPCd7UMP39yUbCjE5f93cUzMFL4xlcVbMFSztCWDbBwbgEjl0zNgoUF1j0G6dV6suCucttnBhMTIrbPTZ_gWAjBdTCMquTB=w640-h192" width="640" /></a></div><br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg-NX0Clyo1tmYhmUA8-rACHkVYDFKRO66F5epi1_xo-Nh8Pw_TXOST9hL8rfZ_fo8FvCIoTQ7kE6qimk8TJU7Xex7y2TZfIKuDZLHli8J3NCqBLmlQ6RsVufq7e0RQakIe3DligeBbNz080lFJyMroxLTj4nOL78NsbM1UzNB0iISJy_BFrMHUBGSOCEcO" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="890" data-original-width="1155" height="309" src="https://blogger.googleusercontent.com/img/a/AVvXsEg-NX0Clyo1tmYhmUA8-rACHkVYDFKRO66F5epi1_xo-Nh8Pw_TXOST9hL8rfZ_fo8FvCIoTQ7kE6qimk8TJU7Xex7y2TZfIKuDZLHli8J3NCqBLmlQ6RsVufq7e0RQakIe3DligeBbNz080lFJyMroxLTj4nOL78NsbM1UzNB0iISJy_BFrMHUBGSOCEcO=w400-h309" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h2 style="clear: both; text-align: left;">Component CSS</h2><div class="separator" style="clear: both; text-align: left;">In the <i>UpdateDeleter.razor </i>page, notice that we have some CSS in the <i><style> . . . </style></i> block. We can move this into a CSS file that only serves the <i>UpdateDelete.razor </i>component. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">In the <i>Pages </i>foldr, create a text file name <i>UpdateDelete.razor.css</i> and add to it the following CSS:</div><div class="separator" style="clear: both; text-align: left;"><br /></div></div></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">fieldset {</span></div></div></div></div></div></div></div><div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> border: 2px solid #000;</span></div></div></div></div></div></div></div><div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> padding-left: 20px;</span></div></div></div></div></div></div></div><div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> margin-bottom: 20px;</span></div></div></div></div></div></div></div><div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">}</span></div></div></div></div></div></div></div></blockquote><div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Meantime, delete the entire <i><style> . . . . </style></i> block in <i>UpdateDeleter.razor.</i> The page should behave just like it did before when you view the delete page.</div><h2 style="clear: both; text-align: left;">SignalR</h2><div class="separator" style="clear: both;">Server-side Blazor uses SignalR to keep a copy of the DOM on the server and to only update changes on the client. Open your Chrome browser development settings and look at the blazor line in the Network tab. This gives you an indication that websockets are used to transmit data between clent and server.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEggPMWhYk13StTYZM59QkkZ5WkfrImMTS_n3NdI5c0lTvbivsrP-Ki__q1j-gRUFhY_hwCNdeFVF3ZI0xgRiavrNY3k5hdmA30Dn9qoaDeQmT4eio50K4C-IAdeHNpsre6LadxfcDyQAOKVTNG_kxW8wcFZQvnKopUVYfFzEBa9pEkXopEgPOhywfzLJIT8" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="615" data-original-width="902" height="436" src="https://blogger.googleusercontent.com/img/a/AVvXsEggPMWhYk13StTYZM59QkkZ5WkfrImMTS_n3NdI5c0lTvbivsrP-Ki__q1j-gRUFhY_hwCNdeFVF3ZI0xgRiavrNY3k5hdmA30Dn9qoaDeQmT4eio50K4C-IAdeHNpsre6LadxfcDyQAOKVTNG_kxW8wcFZQvnKopUVYfFzEBa9pEkXopEgPOhywfzLJIT8=w640-h436" width="640" /></a></div><br />Also, look at the negotiate line in the Network tab.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhR32YtJnUwyjs5UyPGEQl9g0JmFzuGEKJWS0VLqCbNFVrGcsUUoDbXbdq9wWdQziBQGT90SKjWQcP9Oj3Hkkp5JtiYBgqvslbfRFS0WH4kaDQBvuG3ESvJdBI3jZZzkj_bDbDxyEV-_Lvt9ryI799Ft5xE-zW-gzvaYdg47ARO0jh5AVziMZGxAQ7ogTBK" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="536" data-original-width="928" height="370" src="https://blogger.googleusercontent.com/img/a/AVvXsEhR32YtJnUwyjs5UyPGEQl9g0JmFzuGEKJWS0VLqCbNFVrGcsUUoDbXbdq9wWdQziBQGT90SKjWQcP9Oj3Hkkp5JtiYBgqvslbfRFS0WH4kaDQBvuG3ESvJdBI3jZZzkj_bDbDxyEV-_Lvt9ryI799Ft5xE-zW-gzvaYdg47ARO0jh5AVziMZGxAQ7ogTBK=w640-h370" width="640" /></a></div><br />I hope you learned something new in this tutorial and trust that you will build much more sophisticated Blazor apps.</div><div class="separator" style="clear: both;"><br /></div></div></div></div></div></div></div></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-90736728107355382492023-10-08T14:21:00.002-07:002024-02-06T15:10:23.692-08:00Extending Users and Roles with ASP.NET Identity in VS Code<p>In this tutorial, I will demo how to add more data fields to the standard users & roles database. In order to proceed with this tutorial, you need to have the following prerequisites:</p><p></p><ul style="text-align: left;"><li>VS Code</li><li>You have installed .NET 8.0</li><li>You have installed the dotnet-ef tool</li><li>You have installed the dotnet-aspnet-codegenerator tool</li></ul><p></p><p>Companion Video: <a href="https://youtu.be/xo4usBberVA">https://youtu.be/xo4usBberVA</a></p><h2 style="text-align: left;">Getting Started</h2><p>Download the source code for an application that seeds some sample users and roles into an SQLite database from this GitHub repo:</p><p><span style="font-family: courier;">git clone https://github.com/medhatelmasry/Code1stUsersRoles<br />cd Code1stUsersRoles</span></p><p>When you run this app, you will be able to access the privacy page (/privacy) with the following credentials:</p>
<table border="1">
<tbody><tr>
<th>Email</th><th>Password</th><th>Role</th><th>Page</th>
</tr>
<tr>
<td>aa@aa.aa</td>
<td>P@$$w0rd</td>
<td>Admin</td>
<td>/privacy</td>
</tr>
<tr>
<td>mm@mm.mm</td>
<td>P@$$w0rd</td>
<td>Member</td>
<td>/</td></tr></tbody></table><p>This is because the <i>PrivacyModel</i> class in <i>Pages/Privacy.cshtml.cs</i> is annotated with the following:</p><p style="text-align: center;"><span style="font-family: courier;">[Authorize (Roles = "Member, Admin")]</span></p><p>Click on the <i>Register</i> link on the top-right side of your keyboard to add a new user. </p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj16aTXgatdkjcTZ46Ke41R7bAX7fEwy5ORSgZzJNZcqtR-KxddWF6ulat0LRywg9ud4qdH6JlL0R0ju1LwZBiK6b3rUv-4RZT-LMr_LGQM9DHC7Zbdq2rYfKuTRh8jp-wtyzXWL4kpJwRC11jBskuXcyQBVIPXFqQXNzHf4eXTO5drqQdcOpx8NwW7cJpl" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="391" data-original-width="457" height="342" src="https://blogger.googleusercontent.com/img/a/AVvXsEj16aTXgatdkjcTZ46Ke41R7bAX7fEwy5ORSgZzJNZcqtR-KxddWF6ulat0LRywg9ud4qdH6JlL0R0ju1LwZBiK6b3rUv-4RZT-LMr_LGQM9DHC7Zbdq2rYfKuTRh8jp-wtyzXWL4kpJwRC11jBskuXcyQBVIPXFqQXNzHf4eXTO5drqQdcOpx8NwW7cJpl=w400-h342" width="400" /></a></div><div class="separator" style="clear: both; text-align: left;">When you click on the <i>Register</i> button, you will receive a page that looks like this:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjS6zVTLCgjCLfxY24ury6-eUd1Qek0T2Phut2YNuGpcwBDNtYUKCoJx77jmLvuITHBFu76pjxEIz8emFqtoqifkhnvIjs6HNX7lXvraWTEchXrkZEk0vzZR8KvsJvXDb-wRDPJc9_6Yf3L5OuDAwTHnKScczKOk5BaXOnyl_fN7_Np0RUWonKAHyf9Skna" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="178" data-original-width="736" height="154" src="https://blogger.googleusercontent.com/img/a/AVvXsEjS6zVTLCgjCLfxY24ury6-eUd1Qek0T2Phut2YNuGpcwBDNtYUKCoJx77jmLvuITHBFu76pjxEIz8emFqtoqifkhnvIjs6HNX7lXvraWTEchXrkZEk0vzZR8KvsJvXDb-wRDPJc9_6Yf3L5OuDAwTHnKScczKOk5BaXOnyl_fN7_Np0RUWonKAHyf9Skna=w640-h154" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br />Click on <i>Logout</i> in the top-right corner.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Open the application folder in VS Code.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Suppose we want to capture more data about the user, in addition to email and password. Let us assume we want to extend user data with <i>FirstName</i> & <i>LastName</i>.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">An easy way to do this is to create a new class that extends <i>IdentityUser</i> and adds the above properties. Create a <i>Models</i> folder and add a new class named <i>CustomUser</i> to it with the following class code: </div><div class="separator" style="clear: both;"><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">public class CustomUser : IdentityUser {</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> public CustomUser() : base() { }</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> public string? FirstName { get; set; }</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> public string? LastName { get; set; }</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">}</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">We may also wish to extend the standard roles table with these properties:</div><div class="separator" style="clear: both;"><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">Description</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">CreatedDate</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Just as we did with users, we will also create another class for roles that inherits from <i>IdentityRole</i>. In the <i>Models</i> folder, create another class named <i>CustomRole</i> and add to it the following code:</div><div class="separator" style="clear: both;"><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">public class CustomRole : IdentityRole {</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> public CustomRole() : base() { }</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> public CustomRole(string roleName) : base(roleName) { }</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> public CustomRole(string roleName, string description,</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> DateTime createdDate)</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> : base(roleName) {</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> base.Name = roleName;</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> this.Description = description;</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> this.CreatedDate = createdDate;</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> }</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> public string? Description { get; set; }</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> public DateTime CreatedDate { get; set; }</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">}</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><br /></span></div><div class="separator" style="clear: both;">Add the following to <i>Pages/ _ViewImports.cshtml</i>:</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: courier;">@using Code1stUsersRoles.Models</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Edit <i>Data/ApplicationDbContext.cs</i> file and make <i>ApplicationDbContext</i> inherit from <i>IdentityDbContext<CustomUser, CustomRole, string></i>. The <i>ApplicationDbContext</i> class code should look like this:</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><span style="font-family: courier;">public class ApplicationDbContext : IdentityDbContext<CustomUser, CustomRole, string> {</span></div><div class="separator" style="clear: both;"><span style="font-family: courier;"> public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)</span></div><div class="separator" style="clear: both;"><span style="font-family: courier;"> : base(options) { }</span></div><div class="separator" style="clear: both;"><span style="font-family: courier;">}</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Modify <i>Data/ModelBuilderExtensions.cs</i> so that it uses <i>CustomUser</i> instead of <i>IdentityUser</i> & <i>CustomRole</i> instead of <i>IdentityRole</i>. </div><div class="separator" style="clear: both;"><ul style="text-align: left;"><li>When creating a role, add data for Description and CreatedDate.</li><li>When creating a user, add data for FirstName & LastName.</li></ul></div><div class="separator" style="clear: both;">In the <i>Program.cs</i> class, replace <i>IdentityUser</i> with <i>ApplicationUser</i> and <i>IdentityRole</i> with <i>ApplicationRole</i>. The <i>builder.Services.AddIdentity</i>… statement will look like this:<br /><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">builder.Services.AddIdentity<CustomUser, CustomRole>(</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">options => {</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"> options.Stores.MaxLengthForKeys = 128;</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">})</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">.AddEntityFrameworkStores<ApplicationDbContext>()</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">.AddDefaultUI()</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">.AddDefaultTokenProviders()</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">.AddRoles<CustomRole>();</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Edit <i>Pages/Shared/_LoginPartial.cshtml </i>and change:</div><div class="separator" style="clear: both;"><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">@inject SignInManager<IdentityUser> SignInManager</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">@inject UserManager<IdentityUser> UserManager</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both; text-align: center;">TO</div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><br /></div></div></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">@inject SignInManager<CustomUser> SignInManager</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">@inject UserManager<CustomUser> UserManager</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Let us start with a clean database and migration. Therefore, delete <i>app.db</i> and the <i>Data/Migrations </i>folder. </div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Then, execute the following commands from within a terminal window in the root folder of the application:</div><div class="separator" style="clear: both;"><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">dotnet ef migrations add M1 -o Data/Migrations</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">dotnet ef database update</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">At this stage, all the database tables are created and seeded. Let us run our application.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhiWeHNOqmRGPNAZRSuDyd8_MJ5OyrOCLjMLdClEt7xz3OeFs006-NOuA3acfTaQaryPVMMo8cR4fSAksfpJTwMwP3y7GbfCXt3B3DRd8WWIoJ8WE74inNPKacCqNvPtSmDt8Cd1F_jx9A3ow81w6rI2hDz9uUoZDGHn2CJHxofm16Uosww5N-v5IRLNCdp" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="322" data-original-width="727" height="284" src="https://blogger.googleusercontent.com/img/a/AVvXsEhiWeHNOqmRGPNAZRSuDyd8_MJ5OyrOCLjMLdClEt7xz3OeFs006-NOuA3acfTaQaryPVMMo8cR4fSAksfpJTwMwP3y7GbfCXt3B3DRd8WWIoJ8WE74inNPKacCqNvPtSmDt8Cd1F_jx9A3ow81w6rI2hDz9uUoZDGHn2CJHxofm16Uosww5N-v5IRLNCdp=w640-h284" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">To prove that user and role data are successfully seeded, login with any of the below credentials that were previously seeded:</div><div class="separator" style="clear: both;"><br /></div></div></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">Email: aa@aa.aa Password: P@$$w0rd</span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">Email: mm@mm.mm Password: P@$$w0rd</span></div></div></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">The next task we need to accomplish is to modify the registration page so that the application can capture extended data such as <i>FirstName</i> & <i>LastName</i>. ASP.NET provides <i>ASP.NET Core Identity</i> as a Razor Class Library. This means that the registration UI is baked into the assemblies and is surfaced with the <i>.AddDefaultUI() </i>option with the <i>services.AddIdentity() </i>command in <i>Program.cs</i>.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">We need to add some additional packages so that we can scaffold the view for account registration. From within a terminal window at the root of your application, run the following commands: </div><div class="separator" style="clear: both;"><br /></div></div></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design</span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore.Design</span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore.SqlServer</span></div></div></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-family: courier;"><br /></span></div></div><div class="separator" style="border: 2px solid black; clear: both; padding: 5px; text-align: left;">
NOTE:
<p>Visit the following site for more information on scaffolding identity views:<br /><br /><a href="https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-3.0&tabs=netcore-cli">
https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-3.0&tabs=netcore-cli
</a></p></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">If you do not already have the .NET code-generation (scaffolding) tool, execute the following command from within a terminal window:</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: courier;">dotnet tool install -g dotnet-aspnet-codegenerator</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">Here are some useful commands pertaining to the code-generation (scaffolding) tool:</div><div><br /></div></div><div>
<table border="1">
<tbody><tr>
<td>Help with the tool</td>
<td><span style="font-family: courier;">dotnet aspnet-codegenerator identity -h</span></td>
</tr>
<tr>
<td>List all the views that can be scaffolded</td>
<td><span style="font-family: courier;">dotnet aspnet-codegenerator identity --listFiles</span></td>
</tr>
<tr>
<td>Scaffold three views</td>
<td><span style="font-family: courier;">dotnet aspnet-codegenerator identity --files "Account.Register;Account.Login;Account.RegisterConfirmation"</span></td>
</tr>
<tr>
<td>Expose all files</td>
<td>d<span style="font-family: courier;">otnet aspnet-codegenerator identity</span></td>
</tr>
</tbody></table>
</div>
<div>
</div><div><br />Since we need to modify the registration controller and view, we instruct the scaffolder to surface the code used for registration. To do this, we will scaffold three pages that pertain to account registration and login. Run the following command from within a terminal window:</div><div><br /></div><div><span style="font-family: courier;">dotnet aspnet-codegenerator identity --files "Account.Register;Account.Login;Account.RegisterConfirmation" -dc ApplicationDbContext</span></div><div><span style="font-family: courier;"><br /></span></div>
<div style="background: yellow;"><span style="font-family: inherit;"><span>NOTE: If you encounter an error, temporarily comment out the statement "</span>builder.Seed();" in <i>ApplicationDbContext.cs</i> and try the above command again.</span></div>
<div><span style="font-family: courier;"><br /></span></div><div>The above command generates a handful of razor view pages under folder <i>Areas/Identity/Pages/Account</i>.</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh9JxVfxMcddYhDSLixqSce5lQrcAD_b9JRH1sW_j5G5IyEDliT8a6hsJRnMVeTJHclweFe-8TQD0If5I-vE7qQThyVrTW9cqMdrYK2wCcNSESuxA-oioIKq16-cwIJPPjux5qyTgjFUBFwHGZ6fQgSHuql3uVXhiRpi-48D9Iq526rbixhHXa8IFoO0PB7" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="482" data-original-width="365" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEh9JxVfxMcddYhDSLixqSce5lQrcAD_b9JRH1sW_j5G5IyEDliT8a6hsJRnMVeTJHclweFe-8TQD0If5I-vE7qQThyVrTW9cqMdrYK2wCcNSESuxA-oioIKq16-cwIJPPjux5qyTgjFUBFwHGZ6fQgSHuql3uVXhiRpi-48D9Iq526rbixhHXa8IFoO0PB7=w303-h400" width="303" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div>Edit the code-behind file A<i>reas/Identity/Pages/Account/Register.cshtml.cs</i>. </div><div><br /></div><div>Add the following properties to the <i>InputModel</i> class: </div><div><br /></div></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">[Required]</span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">[DataType(DataType.Text)]</span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">[StringLength(50, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 2)]</span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">[Display(Name ="First Name")]</span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">public string FirstName { get; set; }</span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;"><br /></span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">[Required]</span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">[DataType(DataType.Text)]</span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">[StringLength(50, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 2)]</span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">[Display(Name = "Last Name")]</span></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">public string LastName { get; set; }</span></div></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><br /></div><div>In the same file, edit the code in the <i>OnPostAsync()</i> method so that line:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">var user = CreateUser();</span></div><div><br /></div><div>is changed to: </div><div><br /></div></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">var user = new CustomUser {</span></div></div></div></div></div></blockquote></blockquote></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;"> UserName = Input.Email,</span></div></div></div></div></div></blockquote></blockquote></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;"> Email = Input.Email,</span></div></div></div></div></div></blockquote></blockquote></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;"> FirstName = Input.FirstName,</span></div></div></div></div></div></blockquote></blockquote></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;"> LastName = Input.LastName</span></div></div></div></div></div></blockquote></blockquote></blockquote></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><span style="font-family: courier;">};</span></div></div></div></div></div></blockquote></blockquote></blockquote></blockquote></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><br /></div><div>Next, let us update the UI. Edit razor page <i>Areas/Identity/Pages/Account/Register.cshtml</i>. Add the following markup to the form right before the <i>email/username</i> block: </div><div><br /></div></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"><div class="form-floating mb-3"></span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"> <input asp-for="Input.FirstName" class="form-control" autocomplete="firstname" aria-required="true" placeholder="First Name"/></span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"> <label asp-for="Input.FirstName"></label></span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"> <span asp-validation-for="Input.FirstName" class="text-danger"></span></span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"></div></span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"><div class="form-floating mb-3"></span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"> <input asp-for="Input.LastName" class="form-control" autocomplete="lastname" aria-required="true" placeholder="Last Name"/></span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"> <label asp-for="Input.LastName"></label></span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"> <span asp-validation-for="Input.LastName" class="text-danger"></span></span></div></div></div></div></div></div><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><div><span style="font-family: courier;"></div></span></div></div></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div><div><br /></div><div>The code generator added some unnecessary code to <i>Program.cs</i> around line 13. Find the following code in <i>Program.cs</i> and comment it out or delete it:</div><div><br /></div><div><div><span style="font-family: courier; text-decoration: line-through;">builder.Services.AddDefaultIdentity<CustomUser>(options => options.SignIn.RequireConfirmedAccount = true).AddEntityFrameworkStores<ApplicationDbContext>();</span></div></div><div><br /></div><div>Run the web application and click on the <i>Register</i> button on the top-right side.</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg35kJvZAENVl1Td_QzxzovNKel8jI-OKkk3mKAnnrnvplBIGHje5mGci543JShW15ZiSz3yKJYITKDB7_pWz7TLqM-za3ePunLjS_HMfY7bYfurJKc0ew8lM_wnM5CE4zlTZZoTWLydaNoRFMTldoJD0yWA3qBuuPNFURLKjY3WeJzZ7MSf4BrVA_Koyz7" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="574" data-original-width="352" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEg35kJvZAENVl1Td_QzxzovNKel8jI-OKkk3mKAnnrnvplBIGHje5mGci543JShW15ZiSz3yKJYITKDB7_pWz7TLqM-za3ePunLjS_HMfY7bYfurJKc0ew8lM_wnM5CE4zlTZZoTWLydaNoRFMTldoJD0yWA3qBuuPNFURLKjY3WeJzZ7MSf4BrVA_Koyz7=w245-h400" width="245" /></a></div><br />When you click on <i>Register</i>, all user data will be saved in the database. </div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjwGoQn-VtgpwnB48zQ-SC_sR7rJSsVh2_nAUlPogViavHncuB9-Xm28o6KIYWZF3mfsGr77uqNS81wymSoqslKaSZxndsFvbSubR85DBkM3GWvyg1LbKKWbCrHw6u79eWuyLYxHIZLtpOB5865HnHS3OrW2AwrbcAHyHxLO_Gp2bWpgB-1xPUgbtnoMbrL" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="174" data-original-width="761" height="146" src="https://blogger.googleusercontent.com/img/a/AVvXsEjwGoQn-VtgpwnB48zQ-SC_sR7rJSsVh2_nAUlPogViavHncuB9-Xm28o6KIYWZF3mfsGr77uqNS81wymSoqslKaSZxndsFvbSubR85DBkM3GWvyg1LbKKWbCrHw6u79eWuyLYxHIZLtpOB5865HnHS3OrW2AwrbcAHyHxLO_Gp2bWpgB-1xPUgbtnoMbrL=w640-h146" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">We have succeeded in updating the registration page so that additional user data is stored. Thanks for coming this far in this tutorial.</div><div class="separator" style="clear: both; text-align: left;"><br /></div></div></div></div></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-67769054294400302792023-10-01T20:32:00.003-07:002024-02-06T15:36:11.239-08:00Seed Users and Roles using EF Code First approach in ASP.NET Razor Pages <p>In this tutorial, I shall describe the steps you need to follow if you want to use Code First migration to seed both users and roles data. The seeding will be done inside the <i>OnModelCreating() </i>method of the Entity Framework <i>DbContext</i> class. To keep things simple, we will use SQLite.</p><p>In order to proceed with this tutorial you need to have the following prerequisites:</p><p></p><ul style="text-align: left;"><li>VS Code</li><li>You have installed .NET 8.0</li><li>You have installed the dotnet-ef tool</li></ul><div>Source Code: <a href="https://github.com/medhatelmasry/Code1stUsersRoles">https://github.com/medhatelmasry/Code1stUsersRoles</a></div><div>Companion Video: <a href="https://www.youtube.com/watch?v=wiRRyQOYMhU">https://www.youtube.com/watch?v=wiRRyQOYMhU</a></div><p></p><h2 style="text-align: left;">Getting Started</h2><p>In a terminal window, execute the following command to create an ASP.NET Razor Pages application that supports database authentication using the lightweight SQLite database:</p><p><span style="font-family: courier;">dotnet new razor --auth individual -f net8.0 -o Code1stUsersRoles</span></p><p>Change directory to the newly created folder then run the application:</p><p><span style="font-family: courier;">cd Code1stUsersRoles<br />dotnet watch</span></p><p>Click on the Register link on the top-right side of your keyboard to add a new user. </p><p></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEibbWgnOb0AbHkMVT85b7squvIgbE_vyV-kx3YRen0SVcpF0JS8K3Bb48SVz1yAG0UrqjHXToFHzYTgABBTnE4tSMRbYG_Qg6ITPrd9Z_r71bYCA5dZZozwzCWiYNKS4-lyt0yLbzNlRkq5J65H14I2wY3b_Hk9EX5nqZCFrIh2ImRYhTJBcmcHGc3GQt7D" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="420" data-original-width="382" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEibbWgnOb0AbHkMVT85b7squvIgbE_vyV-kx3YRen0SVcpF0JS8K3Bb48SVz1yAG0UrqjHXToFHzYTgABBTnE4tSMRbYG_Qg6ITPrd9Z_r71bYCA5dZZozwzCWiYNKS4-lyt0yLbzNlRkq5J65H14I2wY3b_Hk9EX5nqZCFrIh2ImRYhTJBcmcHGc3GQt7D=w363-h400" width="363" /></a></div></div><br /><div>When you click on the Register button, you will receive a page that looks like this:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgM9ljvpR6glIYTHgTTlw9IG_ZTKFjLZeNFnK381J6nITo6Ov_9_4UTQbs8ExpLXu0UPMQ5qX2Ry1No4v_DzeHvScojLXzy3BDEoc-UOEvwNLgZ3Dcs31g-rKKJW2jzuSMs55GlNi3xKHgO9opPku89znymoMoB_8J8XuzPlcfvX_ZPuaZR3mAfcrFLbww-" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="180" data-original-width="965" height="120" src="https://blogger.googleusercontent.com/img/a/AVvXsEgM9ljvpR6glIYTHgTTlw9IG_ZTKFjLZeNFnK381J6nITo6Ov_9_4UTQbs8ExpLXu0UPMQ5qX2Ry1No4v_DzeHvScojLXzy3BDEoc-UOEvwNLgZ3Dcs31g-rKKJW2jzuSMs55GlNi3xKHgO9opPku89znymoMoB_8J8XuzPlcfvX_ZPuaZR3mAfcrFLbww-=w640-h120" width="640" /></a></div><br />Click on the link “Click here to confirm your account” to simulate email confirmation. Thereafter, login with the newly created account email and password.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjMATAB_wsgDwrAebdrIIQoQXvHYWIatMBWFw7ndBoP4BGetbKJAHWS2-X0FaoKEf3_BRr62MVMQ2G--aak5_opUR5BsMmuvN-PWAla6AiMjZArmUyq_paeSYcCYk5jPW_4o7IfMHFrJX5sYXTk8P-HaLuBBX4hfz4yv5rbkoGWQ6t_g_6DCbdO9i4ZM3nD" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="176" data-original-width="722" height="156" src="https://blogger.googleusercontent.com/img/a/AVvXsEjMATAB_wsgDwrAebdrIIQoQXvHYWIatMBWFw7ndBoP4BGetbKJAHWS2-X0FaoKEf3_BRr62MVMQ2G--aak5_opUR5BsMmuvN-PWAla6AiMjZArmUyq_paeSYcCYk5jPW_4o7IfMHFrJX5sYXTk8P-HaLuBBX4hfz4yv5rbkoGWQ6t_g_6DCbdO9i4ZM3nD=w640-h156" width="640" /></a></div><div><div>Click on <i>Logout</i> in the top-right corner.</div><div><br /></div><div>Open the application folder in VS Code.</div><div><br /></div><div>Create a class named<i> SeedUsersRoles</i> in the <i>Data</i> folder of your application. This will contain seed data for roles, users, and information about users that belong to roles. Below is the code for the <i>SeedUsersRoles</i> class:</div></div></div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">public class SeedUsersRoles {</span></div><div style="text-align: left;"><span style="font-family: courier;"> private readonly List<IdentityRole> _roles;</span></div><div style="text-align: left;"><span style="font-family: courier;"> private readonly List<IdentityUser> _users;</span></div><div style="text-align: left;"><span style="font-family: courier;"> private readonly List<IdentityUserRole<string>> _userRoles;</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public SeedUsersRoles() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> _roles = GetRoles();</span></div><div style="text-align: left;"><span style="font-family: courier;"> _users = GetUsers();</span></div><div style="text-align: left;"><span style="font-family: courier;"> _userRoles = GetUserRoles(_users, _roles);</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;"> public List<IdentityRole> Roles { get { return _roles; } }</span></div><div style="text-align: left;"><span style="font-family: courier;"> public List<IdentityUser> Users { get { return _users; } }</span></div><div style="text-align: left;"><span style="font-family: courier;"> public List<IdentityUserRole<string>> UserRoles { get { return _userRoles; } }</span></div><div style="text-align: left;"><span style="font-family: courier;"> private List<IdentityRole> GetRoles() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> // Seed Roles</span></div><div style="text-align: left;"><span style="font-family: courier;"> var adminRole = new IdentityRole("Admin");</span></div><div style="text-align: left;"><span style="font-family: courier;"> adminRole.NormalizedName = adminRole.Name!.ToUpper();</span></div><div style="text-align: left;"><span style="font-family: courier;"> var memberRole = new IdentityRole("Member");</span></div><div style="text-align: left;"><span style="font-family: courier;"> memberRole.NormalizedName = memberRole.Name!.ToUpper();</span></div><div style="text-align: left;"><span style="font-family: courier;"> List<IdentityRole> roles = new List<IdentityRole>() {</span></div><div style="text-align: left;"><span style="white-space: normal;"><span style="font-family: courier;"><span style="white-space: pre;"> </span>adminRole,</span></span></div><div style="text-align: left;"><span style="white-space: normal;"><span style="font-family: courier;"><span style="white-space: pre;"> </span>memberRole</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> return roles;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> private List<IdentityUser> GetUsers() {</span></div><div style="text-align: left;"><span style="font-family: courier;"> string pwd = "P@$$w0rd";</span></div><div style="text-align: left;"><span style="font-family: courier;"> var passwordHasher = new PasswordHasher<IdentityUser>();</span></div><div style="text-align: left;"><span style="font-family: courier;"> // Seed Users</span></div><div style="text-align: left;"><span style="font-family: courier;"> var adminUser = new IdentityUser {</span></div><div style="text-align: left;"><span style="font-family: courier;"> UserName = "aa@aa.aa",</span></div><div style="text-align: left;"><span style="font-family: courier;"> Email = "aa@aa.aa",</span></div><div style="text-align: left;"><span style="font-family: courier;"> EmailConfirmed = true,</span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> adminUser.NormalizedUserName = adminUser.UserName.ToUpper();</span></div><div style="text-align: left;"><span style="font-family: courier;"> adminUser.NormalizedEmail = adminUser.Email.ToUpper();</span></div><div style="text-align: left;"><span style="font-family: courier;"> adminUser.PasswordHash = passwordHasher.HashPassword(adminUser, pwd);</span></div><div style="text-align: left;"><span style="font-family: courier;"> var memberUser = new IdentityUser {</span></div><div style="text-align: left;"><span style="font-family: courier;"> UserName = "mm@mm.mm",</span></div><div style="text-align: left;"><span style="font-family: courier;"> Email = "mm@mm.mm",</span></div><div style="text-align: left;"><span style="font-family: courier;"> EmailConfirmed = true,</span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> memberUser.NormalizedUserName = memberUser.UserName.ToUpper();</span></div><div style="text-align: left;"><span style="font-family: courier;"> memberUser.NormalizedEmail = memberUser.Email.ToUpper();</span></div><div style="text-align: left;"><span style="font-family: courier;"> memberUser.PasswordHash = passwordHasher.HashPassword(memberUser, pwd);</span></div><div style="text-align: left;"><span style="font-family: courier;"> List<IdentityUser> users = new List<IdentityUser>() {</span></div><div style="text-align: left;"><span style="white-space: normal;"><span style="font-family: courier;"><span style="white-space: pre;"> </span>adminUser,</span></span></div><div style="text-align: left;"><span style="white-space: normal;"><span style="font-family: courier;"><span style="white-space: pre;"> </span>memberUser,</span></span></div><div style="text-align: left;"><span style="font-family: courier;"> };</span></div><div style="text-align: left;"><span style="font-family: courier;"> return users;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;"> private List<IdentityUserRole<string>> GetUserRoles(List<IdentityUser> users, List<IdentityRole> roles) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> // Seed UserRoles</span></div><div style="text-align: left;"><span style="font-family: courier;"> List<IdentityUserRole<string>> userRoles = new List<IdentityUserRole<string>>();</span></div><div style="text-align: left;"><span style="font-family: courier;"> userRoles.Add(new IdentityUserRole<string> {</span></div><div style="text-align: left;"><span style="font-family: courier;"> UserId = users[0].Id,</span></div><div style="text-align: left;"><span style="font-family: courier;"> RoleId = roles.First(q => q.Name == "Admin").Id</span></div><div style="text-align: left;"><span style="font-family: courier;"> });</span></div><div style="text-align: left;"><span style="font-family: courier;"> userRoles.Add(new IdentityUserRole<string> {</span></div><div style="text-align: left;"><span style="font-family: courier;"> UserId = users[1].Id,</span></div><div style="text-align: left;"><span style="font-family: courier;"> RoleId = roles.First(q => q.Name == "Member").Id</span></div><div style="text-align: left;"><span style="font-family: courier;"> });</span></div><div style="text-align: left;"><span style="font-family: courier;"> return userRoles;</span></div><div style="text-align: left;"><span style="font-family: courier;"> }</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span></div></blockquote><div style="text-align: left;"><div><span style="font-family: courier;"><br /></span></div></div><div><div style="text-align: left;">Open <i>Data/ApplicationDbContext.cs</i> in your editor. Add the following <i>OnModelCreating()</i> method to the class:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><span style="font-family: courier;">protected override void OnModelCreating(ModelBuilder builder) {</span></div><div style="text-align: left;"><span style="font-family: courier;"> base.OnModelCreating(builder);</span></div><div style="text-align: left;"><span style="font-family: courier;"> // Use seed method here</span></div><div style="text-align: left;"><span style="font-family: courier;"> SeedUsersRoles seedUsersRoles = new();</span></div><div style="text-align: left;"><span style="font-family: courier;"> builder.Entity<IdentityRole>().HasData(seedUsersRoles.Roles);</span></div><div style="text-align: left;"><span style="font-family: courier;"> builder.Entity<IdentityUser>().HasData(seedUsersRoles.Users);</span></div><div style="text-align: left;"><span style="font-family: courier;"> builder.Entity<IdentityUserRole<string>>().HasData(seedUsersRoles.UserRoles);</span></div><div style="text-align: left;"><span style="font-family: courier;">}</span> </div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"> </div></blockquote><div><div>In the <i>Program.cs</i> class, replace the call to <i>builder.Services.AddDefaultIdentity</i> statement so that it registers <i>IdentityRole</i>. Replace the entire <i>builder.Services.AddDefaultIdentity</i> statement with the following code:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">builder.Services.AddIdentity<IdentityUser, IdentityRole>(</span></div></div><div><div><span style="font-family: courier;">options => {</span></div></div><div><div><span style="font-family: courier;"> options.Stores.MaxLengthForKeys = 128;</span></div></div><div><div><span style="font-family: courier;">})</span></div></div><div><div><span style="font-family: courier;">.AddEntityFrameworkStores<ApplicationDbContext>()</span></div></div><div><div><span style="font-family: courier;">.AddRoles<IdentityRole>()</span></div></div><div><div><span style="font-family: courier;">.AddDefaultUI()</span></div></div><div><div><span style="font-family: courier;">.AddDefaultTokenProviders();</span></div></div></blockquote><div><div><span style="font-family: courier;"><br /></span></div><div>Delete the <i>Data/Migrations</i> folder and the <i>app.db</i> file because we will add new migrations. Thereafter, type the following from a terminal window inside of the root folder of your application:</div><div><br /></div><div><div style="text-align: center;"><span style="font-family: courier;">dotnet ef migrations add M1 -o Data/Migrations</span></div><br /></div><div>We will apply migrations and update the database with the following command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet ef database update</span></div><div><br /></div><div>Now start the application. To prove that user and role data are successfully seeded, login with one of the below credentials that were previously seeded:<br /><br /></div></div><div>
<table border="1">
<tbody><tr>
<th>Email</th>
<th>Password</th>
<th>Role</th>
</tr>
<tr>
<td>aa@aa.aa</td>
<td>P@$$w0rd</td>
<td>Admin</td>
</tr>
<tr>
<td>mm@mm.mm</td>
<td>P@$$w0rd</td>
<td>Member</td>
</tr>
</tbody></table>
</div><div><br /></div><div><div>Add the following above the <i>IndexModel</i> class in <i>Index.cshtml.cs</i> to only allow users that belong to the <i>Member</i> role:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">[Authorize (Roles="Member")]</span></div><div><br /></div><div>Also, add the following above the <i>PrivacyModel</i> class in <i>Privacy.cshtml.cs</i> to only allow users that belong to the <i>Admin</i> role:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">[Authorize (Roles="Admin")]</span></div><div><br /></div><div>Only <i>mm@mm.mm</i> is allowed into the / home page and <i>aa@aa.aa</i> is allowed in the <i>/privacy</i> page.</div><div><br /></div><div>We have succeeded in seeding user and role. Happy Coding.</div></div><div><br /></div><p></p>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-78328181924270285452023-10-01T17:20:00.003-07:002024-02-05T11:16:42.536-08:00Seeding Users and Roles in ASP.NET Razor Pages with RoleManager & UserManager<p>In this tutorial, I shall describe the steps you need to follow if you want seed both users and roles data. We will also look at how you can go about securing an ASP.NET Razor Pages application by user and by roles. To keep things simple, we will use the SQLite database.</p><p>To proceed with this tutorial, you need to have the following prerequisites:</p><p></p><ul style="text-align: left;"><li>VS Code</li><li>You have installed .NET 8.0</li></ul><div>Source Code: <span face="Roboto, Noto, sans-serif" style="background-color: white; color: #0d0d0d; font-size: 15px; white-space-collapse: preserve;"><a href="https://github.com/medhatelmasry/SeedIdentity">https://github.com/medhatelmasry/SeedIdentity</a></span></div><div>Companion Video: <a href="https://youtu.be/sO8zUaWvOxo">https://youtu.be/sO8zUaWvOxo</a></div><p></p><h2 style="text-align: left;">Getting Started</h2><p>In a terminal window, execute the following command to create an ASP.NET Razor Pages application that supports database authentication using the lightweight SQLite database:</p><p style="text-align: center;"><span style="font-family: courier;">dotnet new razor --auth individual -f net8.0 -o SeedIdentity</span></p><p>Change directory to the newly created folder then run the application:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">cd SeedIdentity</span></p><p><span style="font-family: courier;">dotnet watch</span></p></blockquote><p>Click on the <i>Register</i> link on the top-right side of your keyboard to add a new user. </p><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhH4jvma1HCa7BR3UhlffAux9WcEeQK-cnBdFdgWE4g14GcdcgSOvx7hHYXcTiyujPcPf65jsvZ_lg0ZejfJHIRUIQxs52EJgHbwY9G5dM1u81cKH8gutOOJzUj9rwCZ0lrMkLhMUquFCKGQ13rnYlS5Bji9bz1Vupwgo6CMh2SytRT7tC8AG8ThmVg75lN" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="450" data-original-width="251" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEhH4jvma1HCa7BR3UhlffAux9WcEeQK-cnBdFdgWE4g14GcdcgSOvx7hHYXcTiyujPcPf65jsvZ_lg0ZejfJHIRUIQxs52EJgHbwY9G5dM1u81cKH8gutOOJzUj9rwCZ0lrMkLhMUquFCKGQ13rnYlS5Bji9bz1Vupwgo6CMh2SytRT7tC8AG8ThmVg75lN=w223-h400" width="223" /></a></div><br /><br /></div><br /><div style="text-align: left;">When you click on the <i>Register</i> button, you will receive a page that looks like this:</div><div style="text-align: left;"><br /></div></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhkDxj7PHtiK136lfR6IN5lOcY0OGULztrlhfhgSpgrvTJ1sqnUm-TDvLfXX5WumFm0sWEJ9UoOOHcPJJj7kPDmi1TQOrO3OHTx-LNOrI8hFZfW_I3EgMTGPWr54nrcEZcimEeGMzERjFNRivbBZd7kdHm6qAXeIHe0_2wRwTHl5lfV21QOEHe2FfzqZdtV" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="189" data-original-width="973" height="125" src="https://blogger.googleusercontent.com/img/a/AVvXsEhkDxj7PHtiK136lfR6IN5lOcY0OGULztrlhfhgSpgrvTJ1sqnUm-TDvLfXX5WumFm0sWEJ9UoOOHcPJJj7kPDmi1TQOrO3OHTx-LNOrI8hFZfW_I3EgMTGPWr54nrcEZcimEeGMzERjFNRivbBZd7kdHm6qAXeIHe0_2wRwTHl5lfV21QOEHe2FfzqZdtV=w640-h125" width="640" /></a></div><br /><br /><div style="text-align: left;">Click on the link “Click here to confirm your account” to simulate email confirmation. Thereafter, login with the newly created account email and password.</div><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi3-oSkNNr7W3CQKspG7bYsQLglq59f6ZkmzDnjLmOf8wmSxTKafN0xGyVvHYuQxlGaRi4bdZcNa5FjDZ2rHAWHeHvaEru6u3Ce_L-fpE2kMwXeMoQW_9y5HUiPC4bp5KwpGSXL_jRFU09z_gf9ASeTNVycZgS4vQTOQD1KxLzAa0us7iGY00_gN2wyVslW" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="173" data-original-width="729" height="152" src="https://blogger.googleusercontent.com/img/a/AVvXsEi3-oSkNNr7W3CQKspG7bYsQLglq59f6ZkmzDnjLmOf8wmSxTKafN0xGyVvHYuQxlGaRi4bdZcNa5FjDZ2rHAWHeHvaEru6u3Ce_L-fpE2kMwXeMoQW_9y5HUiPC4bp5KwpGSXL_jRFU09z_gf9ASeTNVycZgS4vQTOQD1KxLzAa0us7iGY00_gN2wyVslW=w640-h152" width="640" /></a></div></div><br /><div style="text-align: left;"><div>Click on <i>Logout</i> in the top-right corner.</div><div><br /></div><div>Open the application folder in VS Code.</div><div><br /></div><div>Let us create some sample data for roles and users. Create class named <i>IdentitySeedData</i> in the <i>Data</i> folder, and then add to it the following method: </div><div><br /></div><div><span style="font-family: courier;">public class IdentitySeedData {</span></div><div><span style="font-family: courier;"> public static async Task Initialize(ApplicationDbContext context,</span></div><div><span style="font-family: courier;"> UserManager<IdentityUser> userManager,</span></div><div><span style="font-family: courier;"> RoleManager<IdentityRole> roleManager) {</span></div><div><span style="font-family: courier;"> context.Database.EnsureCreated();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> string asdminRole = "Admin";</span></div><div><span style="font-family: courier;"> string memberRole = "Member";</span></div><div><span style="font-family: courier;"> string password4all = "P@$$w0rd";</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (await roleManager.FindByNameAsync(asdminRole) == null) {</span></div><div><span style="font-family: courier;"> await roleManager.CreateAsync(new IdentityRole(asdminRole));</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (await roleManager.FindByNameAsync(memberRole) == null) {</span></div><div><span style="font-family: courier;"> await roleManager.CreateAsync(new IdentityRole(memberRole));</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (await userManager.FindByNameAsync("aa@aa.aa") == null){</span></div><div><span style="font-family: courier;"> var user = new IdentityUser {</span></div><div><span style="font-family: courier;"> UserName = "aa@aa.aa",</span></div><div><span style="font-family: courier;"> Email = "aa@aa.aa",</span></div><div><span style="font-family: courier;"> PhoneNumber = "6902341234"</span></div><div><span style="font-family: courier;"> };</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var result = await userManager.CreateAsync(user);</span></div><div><span style="font-family: courier;"> if (result.Succeeded) {</span></div><div><span style="font-family: courier;"> await userManager.AddPasswordAsync(user, password4all);</span></div><div><span style="font-family: courier;"> await userManager.AddToRoleAsync(user, asdminRole);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (await userManager.FindByNameAsync("bb@bb.bb") == null) {</span></div><div><span style="font-family: courier;"> var user = new IdentityUser {</span></div><div><span style="font-family: courier;"> UserName = "bb@bb.bb",</span></div><div><span style="font-family: courier;"> Email = "bb@bb.bb",</span></div><div><span style="font-family: courier;"> PhoneNumber = "7788951456"</span></div><div><span style="font-family: courier;"> };</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var result = await userManager.CreateAsync(user);</span></div><div><span style="font-family: courier;"> if (result.Succeeded) {</span></div><div><span style="font-family: courier;"> await userManager.AddPasswordAsync(user, password4all);</span></div><div><span style="font-family: courier;"> await userManager.AddToRoleAsync(user, asdminRole);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (await userManager.FindByNameAsync("mm@mm.mm") == null) {</span></div><div><span style="font-family: courier;"> var user = new IdentityUser {</span></div><div><span style="font-family: courier;"> UserName = "mm@mm.mm",</span></div><div><span style="font-family: courier;"> Email = "mm@mm.mm",</span></div><div><span style="font-family: courier;"> PhoneNumber = "6572136821"</span></div><div><span style="font-family: courier;"> };</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var result = await userManager.CreateAsync(user);</span></div><div><span style="font-family: courier;"> if (result.Succeeded) {</span></div><div><span style="font-family: courier;"> await userManager.AddPasswordAsync(user, password4all);</span></div><div><span style="font-family: courier;"> await userManager.AddToRoleAsync(user, memberRole);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (await userManager.FindByNameAsync("dd@dd.dd") == null) {</span></div><div><span style="font-family: courier;"> var user = new IdentityUser {</span></div><div><span style="font-family: courier;"> UserName = "dd@dd.dd",</span></div><div><span style="font-family: courier;"> Email = "dd@dd.dd",</span></div><div><span style="font-family: courier;"> PhoneNumber = "6041234567"</span></div><div><span style="font-family: courier;"> };</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var result = await userManager.CreateAsync(user);</span></div><div><span style="font-family: courier;"> if (result.Succeeded) {</span></div><div><span style="font-family: courier;"> await userManager.AddPasswordAsync(user, password4all);</span></div><div><span style="font-family: courier;"> await userManager.AddToRoleAsync(user, memberRole);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div><div><br /></div><div>In the <i>Program.cs </i>class, replace the call to <i>builder.Services.AddDefaultIdentity </i>statement so that it uses the new <i>IdentityUser</i> & <i>IdentityRole</i>. Replace the entire statement with the following code:</div><div><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;">builder.Services.AddIdentity<IdentityUser, IdentityRole>(</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;">options => {</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"> options.Stores.MaxLengthForKeys = 128;</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;">})</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;">.AddEntityFrameworkStores<ApplicationDbContext>()</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;">.AddRoles<IdentityRole>()</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;">.AddDefaultUI()</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;">.AddDefaultTokenProviders();</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><br /></div><div>We need to call the I<i>nitialize()</i> method in the <i>IdentitySeedData</i> class from <i>Program.cs</i> so that when the application starts, the users are seeded. In <i>Program.cs</i>, just before “app.Run();” at the bottom, add this code:</div><div><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;">using (var scope = app.Services.CreateScope()) {</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"> var services = scope.ServiceProvider;</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"> var context = services.GetRequiredService<ApplicationDbContext>(); </span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"> context.Database.Migrate();</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"> var userMgr = services.GetRequiredService<UserManager<IdentityUser>>(); </span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"> var roleMgr = services.GetRequiredService<RoleManager<IdentityRole>>(); </span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;"> IdentitySeedData.Initialize(context, userMgr, roleMgr).Wait();</span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-family: courier;">}</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><br /></div><div>In the above code the following takes place:</div><div><ul style="text-align: left;"><li>Instances of <i>ApplicationDbContext</i>, <i>UserManager<IdentityUser></i> & <i>RoleManager<IdentityRole></i> are obtained </li><li>If there are any outstanding migrations, they are automatically executed</li><li>The I<i>dentitySeedData.Initialize() </i>method is called</li></ul></div><div>At this stage, all the database tables are created. However, data is not yet seeded. Let us run our application so that the sample roles and users are seeded in the database. Make sure the application is running. Logout, if you are already logged in.</div><div><br /></div></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgDWafLObrPxd9wIWUp6qoRQTrw9KiMi1KTF3HOvtAIWaDBKbVe24yKzdz_hpVD98Zj8m9SVkJ-1K_ntsfx_KneL0JG1BzZ9-tKhrkfZZPytjvQl9gMR2l06w5lE_KgPSqJhkWMCIWBdvWN7Htf3oR5eH_3UFFsUjJHX5txmi7d1ZgiC-N_CwQt_2Fapl8k" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="186" data-original-width="732" height="162" src="https://blogger.googleusercontent.com/img/a/AVvXsEgDWafLObrPxd9wIWUp6qoRQTrw9KiMi1KTF3HOvtAIWaDBKbVe24yKzdz_hpVD98Zj8m9SVkJ-1K_ntsfx_KneL0JG1BzZ9-tKhrkfZZPytjvQl9gMR2l06w5lE_KgPSqJhkWMCIWBdvWN7Htf3oR5eH_3UFFsUjJHX5txmi7d1ZgiC-N_CwQt_2Fapl8k=w640-h162" width="640" /></a></div><br />To prove that user and role data are successfully seeded, login with one of the below credentials that were previously seeded:</div><div><br /></div><div>
<table border="1">
<tbody><tr>
<th>Email</th>
<th>Password</th>
<th>Role</th>
</tr>
<tr>
<td>aa@aa.aa</td>
<td>P@$$w0rd</td>
<td>Admin</td>
</tr>
<tr>
<td>mm@mm.mm</td>
<td>P@$$w0rd</td>
<td>Member</td>
</tr>
</tbody></table>
</div><div><br /></div><div>We have succeeded in seeding user and role. Happy Coding.</div><div><br /></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-28204456916503970642023-09-25T18:38:00.003-07:002023-09-26T11:30:31.931-07:00Code First development with ASP.NET MVC<p> In this tutorial, you will develop a data driven web application using ASP.NET MVC, SQL Server, and Entity Framework. We shall use Visual Studio Code for our editor. The data model will be based on a <i>Team</i>/<i>Player</i> relationship in sports. We will use SQL Server running in a Docker Container.</p><p>Source code: <a href="https://github.com/medhatelmasry/TeamPlayersMvc">https://github.com/medhatelmasry/TeamPlayersMvc</a></p><h2>Assumptions</h2><p>It is assumed that you have the following installed on your computer:</p><p></p><ul><li>Visual Studio Code</li><li>.NET 7.0 SDK</li><li>Docker Desktop</li></ul><p></p><div><h2>Visual Studio Code Extension</h2><div>Add this Visual Studio Code Extension if you do not have it already:</div></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj1lrZMSmtcSU_fnsE0oZGcd17e7_49wRG_8Q2sDy5jzxeYEBNjRJxL2_p8myOeP_tmMKV7A_-HGKCFMFJMlY5ELQcoAtxERXdYyFscvg1jFOuAX6ob5oDsQhLPpWixzflzaw27Pv9fMXY4D4HKXb_SSAGIefnzMNl6GORpXBXN-eoeH47qk1my11diIIn9" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="240" data-original-width="1164" height="66" src="https://blogger.googleusercontent.com/img/a/AVvXsEj1lrZMSmtcSU_fnsE0oZGcd17e7_49wRG_8Q2sDy5jzxeYEBNjRJxL2_p8myOeP_tmMKV7A_-HGKCFMFJMlY5ELQcoAtxERXdYyFscvg1jFOuAX6ob5oDsQhLPpWixzflzaw27Pv9fMXY4D4HKXb_SSAGIefnzMNl6GORpXBXN-eoeH47qk1my11diIIn9" width="320" /></a></div></div><h2>The Data Model</h2><div>We will use the following class to represent a <i>Team</i>:</div></div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier;">public class Team {</span></div><div><span style="font-family: courier;"> [Key]</span></div><div><span style="font-family: courier;"> public string? TeamName { get; set; }</span></div><div><span style="font-family: courier;"> public string? City { get; set; }</span></div><div><span style="font-family: courier;"> public string? Province { get; set; }</span></div><div><span style="font-family: courier;"> public string? Country { get; set; }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public List<Player>? Players { get; set; }</span></div><div><span style="font-family: courier;">}</span></div></blockquote><div><div><br /></div><div>The primary key is the <i>TeamName</i> and a team has many players.</div><div><br /></div><div>The following class represents a <i>Player</i>:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier;">public class Player {</span></div><div><span style="font-family: courier;"> public int PlayerId { get; set; }</span></div><div><span style="font-family: courier;"> public string? FirstName { get; set; }</span></div><div><span style="font-family: courier;"> public string? LastName { get; set; }</span></div><div><span style="font-family: courier;"> public string? Position { get; set; }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public string? TeamName { get; set; }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> [ForeignKey("TeamName")]</span></div><div><span style="font-family: courier;"> public Team? Team { get; set; }</span></div><div><span style="font-family: courier;">}</span></div></blockquote><div><div><br /></div><div>The primary key is <i>PlayerId</i> and each player must belong to a team.</div><h2>Getting started</h2><div>In a working directory, run the following command in a terminal window to create a Razor Pages web application in a folder names <i>TeamPlayers</i>:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet new mvc -f net7.0 -o TeamPlayersMvc</span></div><div><br /></div><div>Change to the newly created folder with terminal command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">cd </span><span style="font-family: courier;">TeamPlayersMvc</span></div><div><br /></div><div>For the application to work with SQL Server, we will need to add some packages by running the following commands:</div><div><br /></div><div><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore</span></div><div><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore.SqlServer</span></div><div><span style="font-family: courier;">dotnet add package Microsoft.EntityFrameworkCore.Tools<br />dotnet add package Microsoft.EntityFrameworkCore.Design<br /></span></div><div><span style="font-family: courier;"> </span></div><div>We will use code generation to scaffold MVC controllers & views. For that purpose, you will also need to add this package:</div><div><br /></div><div><span style="font-family: courier;">dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design</span></div><div><br /></div><div>If you have not done so already, you will need to globally install the following tools for Entity Framework and Code Generation respectively:</div><div><br /></div><div><span style="font-family: courier;">dotnet tool install -g dotnet-aspnet-codegenerator</span></div><div><span style="font-family: courier;">dotnet tool install -g dotnet-ef</span></div><div><br /></div><div>NOTE: If these tools are already installed, run the above commands while replacing ‘install’ with ‘update’ to get the latest version of the tool.</div><div><br /></div><h2>Creating the model classes</h2><div>Open your app in Visual Studio Code with this command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">code .</span></div><div><br /></div><div>Add to the <i>Models</i> Teamclasses <i>Team</i> & <i>Player</i> mentioned under title “The Data Model” above.</div><h2>The Context Class</h2><div>We will need to create a database context class to work with relational databases using Entity Framework. To this end, create a <i>Data</i> folder. Inside the <i>Data</i> folder, create a class named <i>ApplicationDbContext</i> with the following code:</div><div> </div><div><span style="font-family: courier;">public class ApplicationDbContext : DbContext {</span></div><div><span style="font-family: courier;"> public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)</span></div><div><span style="font-family: courier;"> : base(options) {}</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public DbSet<Team>? Teams { get; set; }</span></div><div><span style="font-family: courier;"> public DbSet<Player>? Players { get; set; }</span></div><div><span style="font-family: courier;">}</span></div><div><br /></div><h2>Seeding the database with sample data</h2><div>It is always useful to have some sample data to visualize what the app does. Therefore, we will create a class dedicated to seeding data. In the <i>Data</i> folder, create a static class named <i>SeedData</i> and add to it the following code that contains sample data for teams and players:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier;">public static class SeedData {</span></div><div><span style="font-family: courier;"> // this is an extension method to the ModelBuilder class</span></div><div><span style="font-family: courier;"> public static void Seed(this ModelBuilder modelBuilder) {</span></div><div><span style="font-family: courier;"> modelBuilder.Entity<Team>().HasData(</span></div><div><span style="font-family: courier;"> GetTeams()</span></div><div><span style="font-family: courier;"> );</span></div><div><span style="font-family: courier;"> modelBuilder.Entity<Player>().HasData(</span></div><div><span style="font-family: courier;"> GetPlayers()</span></div><div><span style="font-family: courier;"> );</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> public static List<Team> GetTeams() {</span></div><div><span style="font-family: courier;"> List<Team> teams = new List<Team>() {</span></div><div><span style="font-family: courier;"> new Team() { // 1</span></div><div><span style="font-family: courier;"> TeamName="Canucks",</span></div><div><span style="font-family: courier;"> City="Vancouver",</span></div><div><span style="font-family: courier;"> Province="BC",</span></div><div><span style="font-family: courier;"> Country="Canada",</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Team() { //2</span></div><div><span style="font-family: courier;"> TeamName="Sharks",</span></div><div><span style="font-family: courier;"> City="San Jose",</span></div><div><span style="font-family: courier;"> Province="CA",</span></div><div><span style="font-family: courier;"> Country="USA",</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Team() { // 3</span></div><div><span style="font-family: courier;"> TeamName="Oilers",</span></div><div><span style="font-family: courier;"> City="Edmonton",</span></div><div><span style="font-family: courier;"> Province="AB",</span></div><div><span style="font-family: courier;"> Country="Canada",</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Team() { // 4</span></div><div><span style="font-family: courier;"> TeamName="Flames",</span></div><div><span style="font-family: courier;"> City="Calgary",</span></div><div><span style="font-family: courier;"> Province="AB",</span></div><div><span style="font-family: courier;"> Country="Canada",</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Team() { // 5</span></div><div><span style="font-family: courier;"> TeamName="Leafs",</span></div><div><span style="font-family: courier;"> City="Toronto",</span></div><div><span style="font-family: courier;"> Province="ON",</span></div><div><span style="font-family: courier;"> Country="Canada",</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Team() { // 6</span></div><div><span style="font-family: courier;"> TeamName="Ducks",</span></div><div><span style="font-family: courier;"> City="Anaheim",</span></div><div><span style="font-family: courier;"> Province="CA",</span></div><div><span style="font-family: courier;"> Country="USA",</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Team() { // 7</span></div><div><span style="font-family: courier;"> TeamName="Lightening",</span></div><div><span style="font-family: courier;"> City="Tampa Bay",</span></div><div><span style="font-family: courier;"> Province="FL",</span></div><div><span style="font-family: courier;"> Country="USA",</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Team() { // 8</span></div><div><span style="font-family: courier;"> TeamName="Blackhawks",</span></div><div><span style="font-family: courier;"> City="Chicago",</span></div><div><span style="font-family: courier;"> Province="IL",</span></div><div><span style="font-family: courier;"> Country="USA",</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> };</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> return teams;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public static List<Player> GetPlayers() {</span></div><div><span style="font-family: courier;"> List<Player> players = new List<Player>() {</span></div><div><span style="font-family: courier;"> new Player {</span></div><div><span style="font-family: courier;"> PlayerId = 1,</span></div><div><span style="font-family: courier;"> FirstName = "Sven",</span></div><div><span style="font-family: courier;"> LastName = "Baertschi",</span></div><div><span style="font-family: courier;"> TeamName = "Canucks",</span></div><div><span style="font-family: courier;"> Position = "Forward"</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Player {</span></div><div><span style="font-family: courier;"> PlayerId = 2,</span></div><div><span style="font-family: courier;"> FirstName = "Hendrik",</span></div><div><span style="font-family: courier;"> LastName = "Sedin",</span></div><div><span style="font-family: courier;"> TeamName = "Canucks",</span></div><div><span style="font-family: courier;"> Position = "Left Wing"</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Player {</span></div><div><span style="font-family: courier;"> PlayerId = 3,</span></div><div><span style="font-family: courier;"> FirstName = "John",</span></div><div><span style="font-family: courier;"> LastName = "Rooster",</span></div><div><span style="font-family: courier;"> TeamName = "Flames",</span></div><div><span style="font-family: courier;"> Position = "Right Wing"</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> new Player {</span></div><div><span style="font-family: courier;"> PlayerId = 4,</span></div><div><span style="font-family: courier;"> FirstName = "Bob",</span></div><div><span style="font-family: courier;"> LastName = "Plumber",</span></div><div><span style="font-family: courier;"> TeamName = "Oilers",</span></div><div><span style="font-family: courier;"> Position = "Defense"</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> };</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> return players;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div></blockquote><div><div><br /></div><div>Note that the <i>SeedData</i> class is static because it contains an extension method named <i>Seed()</i> to <i>ModelBuilder</i>.</div><div><br /></div><div>The <i>Seed()</i> method needs to be called from somewhere. The most appropriate place is the <i>ApplicationDbContext</i> class. Add the following <i>OnModelCreating()</i> method to the <i>ApplicationDbContext</i> class:</div><div><br /></div><div><span style="font-family: courier;">protected override void OnModelCreating(ModelBuilder builder)</span></div><div><span style="font-family: courier;">{</span></div><div><span style="font-family: courier;"> base.OnModelCreating(builder);</span></div><div><span style="font-family: courier;"> builder.Entity<Player>().Property(m => m.TeamName).IsRequired();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> builder.Entity<Team>().Property(p => p.TeamName).HasMaxLength(30);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> builder.Entity<Team>().ToTable("Team");</span></div><div><span style="font-family: courier;"> builder.Entity<Player>().ToTable("Player");</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> builder.Seed();</span></div><div><span style="font-family: courier;">} </span></div><div><br /></div><div>In addition to seeding data, the above code ensures the following:</div><div><ul><li><i>TeamName</i> is required</li><li>The maximum length of <i>TeamName</i> is 30 characters</li><li>The names of the tables that get created in the database are <i>Team</i> & <i>Player</i>. Otherwise, the names get created as <i>Teams</i> & <i>Players</i>.</li></ul></div><h2>The database</h2><div>You can use any SQL Server database you wish. In my case, so that this app works on Linux, Windows, Mac Intel, and Mac M1, I will run SQL Server in a Docker container. To run SQL Server in a Docker container, run the following command:</div><div><br /></div><div><span style="font-family: courier;">docker run --cap-add SYS_PTRACE -e ACCEPT_EULA=1 -e MSSQL_SA_PASSWORD=SqlPassword! -p 1444:1433 --name azsql -d mcr.microsoft.com/azure-sql-edge</span></div><div><br /></div><div>The connection string to the database is setup in the <i>appsettings.json</i> (or <i>appsetting.Development.json</i>) file. Edit this file and make the following highlighted updates to it:</div><div><br /></div><div>{</div><div> "Logging": {</div><div> "LogLevel": {</div><div> "Default": "Information",</div><div> "Microsoft.AspNetCore": "Warning"</div><div> }</div><div> },</div><div> "AllowedHosts": "*"<span style="background-color: #fcff01;">,</span></div><div><span style="background-color: #fcff01;"> "ConnectionStrings": {</span></div><div><span style="background-color: #fcff01;"> "DefaultConnection": "Server=tcp:127.0.0.1,1444;Database=TeamPlayersDB;UID=sa;PWD=SqlPassword!;TrustServerCertificate=True;"</span></div><div> }</div><div>}</div><div> </div><div><br /></div><div>To make our app work with SQL Server, you will need to add the following code to <i>Program.cs</i> just before “var app = builder.Build();”: </div><div><br /></div><div><span style="font-family: courier;">string connStr = builder.Configuration.GetConnectionString("DefaultConnection")!;</span></div><div><span style="font-family: courier;">builder.Services.AddDbContext<ApplicationDbContext>(</span></div><div><span style="font-family: courier;"> options => options.UseSqlServer(connStr)</span></div><div><span style="font-family: courier;">);</span></div><div><br /></div><h2>Migrations</h2><div>We can now create a migration named <i>m1</i> with:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet ef migrations add M1 -o Data/Migrations</span></div><div style="text-align: center;"><br /></div><div>This creates a migrations file in <i>Data/Migrations</i> folder. To execute the migrations and create the database and seed data, run the following command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet ef database update</span></div><div><br /></div><div>If all goes well and no errors are generated, we can assume that a database named <i>TeamPlayersDB</i> was created, and data is seeded into tables <i>Team</i> & <i>Player</i>.</div><div><br /></div><div>NOTE: If you wish to drop the database for whatever reason, you can run command: <span style="font-family: courier;">dotnet ef database drop</span></div><div> </div><div>This is what the tables in the database look like:</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEikeeccENbmkMzsTu1fqgdLvMNUh6ox_9PI8E4cdLpSPGPKeXp-1ajirV5jOV4sN5wfvXXb8llLsJThtLqMG6eG9jWYUHQPIoThyhTiedKKniWWPqMW_xKJvt7CjETdD83EvUBLdPzpPUsBL4OTJxdI5q8fcefs70NwNFv-vi_ZxliX-5zv7Z36FLcO1w" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="488" data-original-width="2090" height="150" src="https://blogger.googleusercontent.com/img/a/AVvXsEikeeccENbmkMzsTu1fqgdLvMNUh6ox_9PI8E4cdLpSPGPKeXp-1ajirV5jOV4sN5wfvXXb8llLsJThtLqMG6eG9jWYUHQPIoThyhTiedKKniWWPqMW_xKJvt7CjETdD83EvUBLdPzpPUsBL4OTJxdI5q8fcefs70NwNFv-vi_ZxliX-5zv7Z36FLcO1w=w640-h150" width="640" /></a></div><h2>Scaffolding Teams & Players controller and views</h2><div>To incorporate pages into our app that allow us to manage <i>Team</i> & <i>Player</i> data, we will scaffold the necessary controller & views using the <i>aspnet-codegenerator </i>utility. Run the following command from a terminal window in the root of the project to generate files pertaining to teams and players respectively:</div><div><br /></div><div><div><span style="font-family: courier;">dotnet aspnet-codegenerator controller -name TeamsController -outDir Controllers -m Team -dc ApplicationDbContext -udl -scripts</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">dotnet aspnet-codegenerator controller -name PlayersController -outDir Controllers -m Player -dc ApplicationDbContext -udl -scripts</span></div></div><div><br /></div><div>This produces controllers in the Controllers folder and fiews files in folders <i>Views/Teams</i> & <i>Views/Players</i> respectively. To add menu items on the home page that point to <i>Team</i> & <i>Player</i> pages, edit <i>Views/Shared/_Layout.cshtml </i>and add the following HTML to the <i><ul></i> block around line 28:</div><div><br /></div><div><div><span style="font-family: courier;"><li class="nav-item"></span></div><div><span style="font-family: courier;"> <a class="nav-link text-dark" asp-area="" asp-controller="Teams" asp-action="Index">Teams</a></span></div><div><span style="font-family: courier;"></li></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><li class="nav-item"></span></div><div><span style="font-family: courier;"> <a class="nav-link text-dark" asp-area="" asp-controller="Players" asp-action="Index">Players</a></span></div><div><span style="font-family: courier;"></li></span></div></div><h2>The final product</h2><div>Run the web app and notice the main menu:</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjEi8tpDL9am70yr0iFp6FPl-p3hTg2bEa1LvmxuKA1MhKWRvW6zLDgehrrLyPM73bkFgFXLK2B6PLFGKRARbce1S_pQ-wCU2aAUJi5ZxVg8hgSHDhq_cbDUmAX03AEsDOAVTaOvk39J2G50XgliSmGGFHnzmLyIr1TQwY3vw5kDtViHbBzaJ1Rlsbu8gn0" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="187" data-original-width="572" height="105" src="https://blogger.googleusercontent.com/img/a/AVvXsEjEi8tpDL9am70yr0iFp6FPl-p3hTg2bEa1LvmxuKA1MhKWRvW6zLDgehrrLyPM73bkFgFXLK2B6PLFGKRARbce1S_pQ-wCU2aAUJi5ZxVg8hgSHDhq_cbDUmAX03AEsDOAVTaOvk39J2G50XgliSmGGFHnzmLyIr1TQwY3vw5kDtViHbBzaJ1Rlsbu8gn0" width="320" /></a></div><br /><br /></div><br />Click on <i>Teams</i>:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhTTHSZ58VFmoYo0nOlsVsUFLki_pDZdFA-bIE1gRXzjv26zlcuXWT5gW9X4pR4G2xM7M0O0l7JNEiW4jLLR6rDQHI0y-Ctv0J4LBAJ8Cnp-75NLJVKPl8PIq-Hy2zF42vvlD_MTKCCO9Ne4z07NkpC5dRapCjOi5I-5r7mXzD24YyDOMzoEabqsA6XFw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="730" data-original-width="1302" height="224" src="https://blogger.googleusercontent.com/img/a/AVvXsEhTTHSZ58VFmoYo0nOlsVsUFLki_pDZdFA-bIE1gRXzjv26zlcuXWT5gW9X4pR4G2xM7M0O0l7JNEiW4jLLR6rDQHI0y-Ctv0J4LBAJ8Cnp-75NLJVKPl8PIq-Hy2zF42vvlD_MTKCCO9Ne4z07NkpC5dRapCjOi5I-5r7mXzD24YyDOMzoEabqsA6XFw=w400-h224" width="400" /></a></div><div><br /></div>Click on <i>Players</i>:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh8dB3j5SjmMv4Hcbiod2x2xTm01JrHH4aDV5o4rNbQj2jOOtFlP9VxelD3r2hvHTLEzwi4C4LTTWk5oMen-NgM6ZF57tV01w6gzJXmBIyPZ7r1ae5dh84bvOH71hLHI0GW6xXD7hpu4PUGK6pyn_wDHoSbtM-t1iR-Z19aJEOhsFEzabzRTQg7nCk1BA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="596" data-original-width="1798" height="133" src="https://blogger.googleusercontent.com/img/a/AVvXsEh8dB3j5SjmMv4Hcbiod2x2xTm01JrHH4aDV5o4rNbQj2jOOtFlP9VxelD3r2hvHTLEzwi4C4LTTWk5oMen-NgM6ZF57tV01w6gzJXmBIyPZ7r1ae5dh84bvOH71hLHI0GW6xXD7hpu4PUGK6pyn_wDHoSbtM-t1iR-Z19aJEOhsFEzabzRTQg7nCk1BA=w400-h133" width="400" /></a></div><br /><h2>Conclusion</h2></div><div>You just learned how to use the code-first database approach with ASP.NET MVC. The same priciples work with ASP.NET Razor Pages. </div><div><br /></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-26139587229027873252023-09-18T16:48:00.004-07:002023-09-18T16:57:01.715-07:00Connect Azure OpenAI Service to your custom SQL Server dataIn this tutorial, we will lear how to connect your custom SQL Server data to Azure OpenAI. You need to have an active Azure subscription in order to proceed with this tutorial.<h2 style="text-align: left;">Create Azure Cognitive Search</h2><div>Point your browser to <a href="https://portal.azure.com/">https://portal.azure.com/</a>. Enter 'search' in the search field at the top of the page, then click on "Cognitive Search" from the short list of services.</div><div> <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgtxxK2AbmVdZB0g63N-nTO9Ab3kgKRLQCNQ34JVjypR1wD1LjVgdi_kWdlFdhsLfYpeRtJTX0X1n6KZRXktH5LNsCKeEPLZYtdJyslKhfyCPjlAdmz7U0I3b38fYXPyYq1w9CJr1xf5WVn6YTFgkMPhqlfV7wfyNnLyYvpyIFLzEgzAibfwNdtxTP20XCB" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="618" data-original-width="1574" height="158" src="https://blogger.googleusercontent.com/img/a/AVvXsEgtxxK2AbmVdZB0g63N-nTO9Ab3kgKRLQCNQ34JVjypR1wD1LjVgdi_kWdlFdhsLfYpeRtJTX0X1n6KZRXktH5LNsCKeEPLZYtdJyslKhfyCPjlAdmz7U0I3b38fYXPyYq1w9CJr1xf5WVn6YTFgkMPhqlfV7wfyNnLyYvpyIFLzEgzAibfwNdtxTP20XCB=w400-h158" width="400" /></a></div><br /></div><div>On the resulting "Azure AI services | Cognitive search" page, click on the <i>Create</i> button.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhgwakJUIqWja0wzGBH7z_sckS-JM2w267jPPz8_p4RcYMGlRmWXq0r_BaRzJPE01t8OYmkXdeM3oemBi3uKyZ1cxDHA5CDCTss2E2Rq5vnQZ33PAd5aZYgwx_uzHGUXayZTFc_Ij3RAVVr1mn-rK3qhWFzXC_iw3VHy1Dm3b4Qgncvb0fW9n2JQQvYvD-V" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="364" data-original-width="1744" height="84" src="https://blogger.googleusercontent.com/img/a/AVvXsEhgwakJUIqWja0wzGBH7z_sckS-JM2w267jPPz8_p4RcYMGlRmWXq0r_BaRzJPE01t8OYmkXdeM3oemBi3uKyZ1cxDHA5CDCTss2E2Rq5vnQZ33PAd5aZYgwx_uzHGUXayZTFc_Ij3RAVVr1mn-rK3qhWFzXC_iw3VHy1Dm3b4Qgncvb0fW9n2JQQvYvD-V=w400-h84" width="400" /></a></div><br />On the "Create a search service" page:</div><div><br /></div><div><ul style="text-align: left;"><li>choose a suitable <i>subscription</i> if you have more than one</li><li>create a new resource group named <i>openai-and-sql-server</i></li><li>let the "Service name" be <i>openai-and-sql-server-service-name</i></li><li>let the location be a data center closer to where you are. In my case I chose "East US".</li><li>leave the "Pricing tier" as <i>Standard</i></li></ul><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhjPVsqUDduwZ9EVy8PAuZKk68LJ4FWiWkBO_VL7bgijAFV5oqmcsu80C_YMljrTm6Lo5r9nYC9C0i1I0S3CbjfatH514CQ4GfnBkQq7IZgKgjvORVKehHcaiDZ_biQwF9fU8S_u6805QIlDO8e-jtQBLs00ttHq1EmzVKBYpZbVNmxLrohUMjDkAIfj4tE" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="766" data-original-width="1226" height="250" src="https://blogger.googleusercontent.com/img/a/AVvXsEhjPVsqUDduwZ9EVy8PAuZKk68LJ4FWiWkBO_VL7bgijAFV5oqmcsu80C_YMljrTm6Lo5r9nYC9C0i1I0S3CbjfatH514CQ4GfnBkQq7IZgKgjvORVKehHcaiDZ_biQwF9fU8S_u6805QIlDO8e-jtQBLs00ttHq1EmzVKBYpZbVNmxLrohUMjDkAIfj4tE=w400-h250" width="400" /></a></div><br />Click on the blue "Review + create" button, then click on blue "Create" button. Once deployment is complete, click on the blue "Go to resource" button. This takes you to the page of the search resource that you just created.</div></div><div><br /></div><div>Click on the blue Import button under "Connect your data".</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgMkmbwJWvoCWeFjn1wCAuof-ybkrSExHhk4IU6cTOMKzbvtuJ0VW-U2KKJNJH7XSAgPdqite-BYqbE6_RzF9zXyKaS_UXeoXWJXKpd0aRcpa0pACd-vm1XNkb_JmGEUTveZY0459An8VnzZg6ZgLNbXMx8RDSEyY7ff6OaJ4qK9vw9aqPUCCfEW9eDLQcd" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1246" data-original-width="1810" height="275" src="https://blogger.googleusercontent.com/img/a/AVvXsEgMkmbwJWvoCWeFjn1wCAuof-ybkrSExHhk4IU6cTOMKzbvtuJ0VW-U2KKJNJH7XSAgPdqite-BYqbE6_RzF9zXyKaS_UXeoXWJXKpd0aRcpa0pACd-vm1XNkb_JmGEUTveZY0459An8VnzZg6ZgLNbXMx8RDSEyY7ff6OaJ4qK9vw9aqPUCCfEW9eDLQcd=w400-h275" width="400" /></a></div><br /></div><div>On the import data page, let "Data Source" be <i>Samples</i>. Then click on <i>realestate-us-sample</i>.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhp2lX-D1otzAHbytpod4Oa-Z-ITvw2qpoJHc2HCRpQXmLkUiN_LStuGHWwe5PUkkM1BwPiqBXxDqESfMc1Jn2G3MA9SLiWwtkKXlp4mZN5hrptZpc97WLVHjjsdmD2_aspEzXIKQcPLpjiuXKqwC9VDyN7GkUhdb0ozjgGREnLoZLqzWLU5ZZEy-Z-3uVx" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="586" data-original-width="2278" height="165" src="https://blogger.googleusercontent.com/img/a/AVvXsEhp2lX-D1otzAHbytpod4Oa-Z-ITvw2qpoJHc2HCRpQXmLkUiN_LStuGHWwe5PUkkM1BwPiqBXxDqESfMc1Jn2G3MA9SLiWwtkKXlp4mZN5hrptZpc97WLVHjjsdmD2_aspEzXIKQcPLpjiuXKqwC9VDyN7GkUhdb0ozjgGREnLoZLqzWLU5ZZEy-Z-3uVx=w640-h165" width="640" /></a></div>Click on the blue "Next: Add cognitive skills (Optional)" button at the bottom of the page.</div><div><br /></div><div>On the "Import data" page, click on the blue "Skip to: Customize target index" at the bottom of the page.</div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjcRb5jwmJoCW9P0ckW_8qco6uKeewy4xvPn2dw1tBOXp-v8EykAmo7u1VqAnKdUtUFiuwPgs6x5llnRhOv6c55jLMEo_seoLXwm9Gu4A7iqDEQlSdjQYRi3RtYEo2eFJeah6BWRAobCGzYOTwn8CfKRwWxCzKmgEtiO5gjiZzHnmdEo0zYTVWizXvKq0wA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="910" data-original-width="1972" height="296" src="https://blogger.googleusercontent.com/img/a/AVvXsEjcRb5jwmJoCW9P0ckW_8qco6uKeewy4xvPn2dw1tBOXp-v8EykAmo7u1VqAnKdUtUFiuwPgs6x5llnRhOv6c55jLMEo_seoLXwm9Gu4A7iqDEQlSdjQYRi3RtYEo2eFJeah6BWRAobCGzYOTwn8CfKRwWxCzKmgEtiO5gjiZzHnmdEo0zYTVWizXvKq0wA=w640-h296" width="640" /></a></div></div><br /></div><div style="text-align: left;">You will be taken to the "Customize target index" tab. Click on the blue "Next: Create an indexer" button at the bottom of the page.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEixdqiab6uQoeP-22dfohNm4FrBBKbJHbkqowr2gkqh5VYI8bZkdgqUqDqBIJn1_WJK7cKKYGSEJDnIgRrs2fG4ygAxxgb8hghLlWq3eo8aTNA2D90NUUtu6BFsTRygQ8Ogl0Z0M_kVzbFwZ3g532MBBVEbeyBPKIP08oO_gVkPtGMNxtunUDcYTghEoyCT" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1110" data-original-width="2076" height="342" src="https://blogger.googleusercontent.com/img/a/AVvXsEixdqiab6uQoeP-22dfohNm4FrBBKbJHbkqowr2gkqh5VYI8bZkdgqUqDqBIJn1_WJK7cKKYGSEJDnIgRrs2fG4ygAxxgb8hghLlWq3eo8aTNA2D90NUUtu6BFsTRygQ8Ogl0Z0M_kVzbFwZ3g532MBBVEbeyBPKIP08oO_gVkPtGMNxtunUDcYTghEoyCT=w640-h342" width="640" /></a></div></div><br />In the "Create an indexer" tab, click on the blue <i>Submit</i> button.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjt5vL5LbvEZB4HqAEJah652b6uzGEsUmj29jMeTDhf1JFmfs503Tvjx_JD99vyDfyvdOYANs3u479c6EQWZzfvHd_YiSWB3Jhg9iNRmbY4gQMtTAEoEM7jGHnHURsTLbzoELtzifgWZLajs39q3T8bKG--iYjPiDh85FzSiW7BRyL-dJELWByrYjk0vZC7" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1088" data-original-width="2088" height="334" src="https://blogger.googleusercontent.com/img/a/AVvXsEjt5vL5LbvEZB4HqAEJah652b6uzGEsUmj29jMeTDhf1JFmfs503Tvjx_JD99vyDfyvdOYANs3u479c6EQWZzfvHd_YiSWB3Jhg9iNRmbY4gQMtTAEoEM7jGHnHURsTLbzoELtzifgWZLajs39q3T8bKG--iYjPiDh85FzSiW7BRyL-dJELWByrYjk0vZC7=w640-h334" width="640" /></a></div></div><br />In your main service page, click on <i>indexers</i> on the left side. Wait until the indexer you just created shows that it is successfully provisioned.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgH9707WtOSVddSbSJaDa6veDdBH3zPkol055OB9BdfweypV-bUBWC8dP4ADSRDGLJdgyxlwXkSnFhHXy8oAT07-ArblRrumwbacHQpTceVXg0qZUfaiOegRq_Qg_tG7A8-lxXTtPsfSlQi8sOuESpDlQsBYryzAA7sZR5Qmo7J1gu_AIKtctTONrlxiwfZ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1096" data-original-width="1152" height="381" src="https://blogger.googleusercontent.com/img/a/AVvXsEgH9707WtOSVddSbSJaDa6veDdBH3zPkol055OB9BdfweypV-bUBWC8dP4ADSRDGLJdgyxlwXkSnFhHXy8oAT07-ArblRrumwbacHQpTceVXg0qZUfaiOegRq_Qg_tG7A8-lxXTtPsfSlQi8sOuESpDlQsBYryzAA7sZR5Qmo7J1gu_AIKtctTONrlxiwfZ=w400-h381" width="400" /></a></div><br />Click on <i>Overview</i> on the left-side menu, then click on the "Search explorer" link.</div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh1_SbbGzFBVBSQ_p9mu1Jy-cMNlS_NWQhPTEWF0EB1TR6GgC0OqRvgrlfpkfEWokM4NBRfy7SUzBnje2qz3E-f4TuPkQvcxlwM3sHDfWmQryYB_dVMlT9gccT2H3y3cpYouGP0otlNJWREjt4CC4GJEYkuodFKvOus_YSh28OBI0CrVtTjxPkL-O92a7J7" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="644" data-original-width="2244" height="184" src="https://blogger.googleusercontent.com/img/a/AVvXsEh1_SbbGzFBVBSQ_p9mu1Jy-cMNlS_NWQhPTEWF0EB1TR6GgC0OqRvgrlfpkfEWokM4NBRfy7SUzBnje2qz3E-f4TuPkQvcxlwM3sHDfWmQryYB_dVMlT9gccT2H3y3cpYouGP0otlNJWREjt4CC4GJEYkuodFKvOus_YSh28OBI0CrVtTjxPkL-O92a7J7=w640-h184" width="640" /></a></div><br />On the "Search explorer" page, we can query the database. The Index field is defaulted to the index that was just created. In the "Query string" field, enter s<i>earch=condo</i>, then click on the blue <i>Search</i> button. You will see results for condos.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhwSp8sK1p2gMt19ZBzuP42tkCSZ4ur5sCAQxWmSLJ0UGNHE521rpj2F0Xal8l3eVJDRsVzeA3zuPEdh7n-Exb59OPf8NU9JAeLOyf6hZAuCB2CNuP8RIEGj1b98EFlwFQmsWMAVTKhYyZGYRoz380ONba710wy2chTF1M1TMoc_kTVTRiqvLR6M4bj6pfy" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1400" data-original-width="1600" height="350" src="https://blogger.googleusercontent.com/img/a/AVvXsEhwSp8sK1p2gMt19ZBzuP42tkCSZ4ur5sCAQxWmSLJ0UGNHE521rpj2F0Xal8l3eVJDRsVzeA3zuPEdh7n-Exb59OPf8NU9JAeLOyf6hZAuCB2CNuP8RIEGj1b98EFlwFQmsWMAVTKhYyZGYRoz380ONba710wy2chTF1M1TMoc_kTVTRiqvLR6M4bj6pfy=w400-h350" width="400" /></a></div><br /><h3 style="text-align: left;">Azure OpenAI Service</h3></div><div>We are now ready to connect this cognitive search service with OpenAI. Click on the Home link in the top left corner of the page.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjbvirRK-Ya0TrqWtSyV7g-QS5k7P3iM-obEhPSWqtmTtVu1HgNNmsdf_IywaHrAhs1N5WdBzoeh5eb7HKw61DSd-iL8VGv2E7LPAJEprz5x6lFyGStFwwqdyPpboEqMwa4-chif1bswWfyhmGAQKg0N8WH6aRuyWV1JLbhbgv1w1wXtOGOmgIuDldXOOO1" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="392" data-original-width="1606" height="156" src="https://blogger.googleusercontent.com/img/a/AVvXsEjbvirRK-Ya0TrqWtSyV7g-QS5k7P3iM-obEhPSWqtmTtVu1HgNNmsdf_IywaHrAhs1N5WdBzoeh5eb7HKw61DSd-iL8VGv2E7LPAJEprz5x6lFyGStFwwqdyPpboEqMwa4-chif1bswWfyhmGAQKg0N8WH6aRuyWV1JLbhbgv1w1wXtOGOmgIuDldXOOO1=w640-h156" width="640" /></a></div><br />In the filter field at the top, enter the word <i>openai</i>, then select Azure OpenAI.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjS9dOkLkQEJL-nXNqLyaqUoG-w1bX2OWZpZuQBdQRGVyXuQV1f7jAZDcmckEDYEFVP4_ZnkctxH6VaWX1-vLFSejyLQlTP4tP_Lwdm448jaYyhqAiCH6uia_abKXDpet7fO6qdDSnhe6KR626LJ_wsp5AJzSGj9I805vZvdbhlIvxfh8hUuBMocTMawlsa" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="570" data-original-width="2108" height="174" src="https://blogger.googleusercontent.com/img/a/AVvXsEjS9dOkLkQEJL-nXNqLyaqUoG-w1bX2OWZpZuQBdQRGVyXuQV1f7jAZDcmckEDYEFVP4_ZnkctxH6VaWX1-vLFSejyLQlTP4tP_Lwdm448jaYyhqAiCH6uia_abKXDpet7fO6qdDSnhe6KR626LJ_wsp5AJzSGj9I805vZvdbhlIvxfh8hUuBMocTMawlsa=w640-h174" width="640" /></a></div><br />Click on the "+ Create" link. </div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiIHnD4qggzpVbVIr4pAPqfZ_Jgeu9SkG1YTd8iPFrl1qZglVBcIM8Veb0OY6HsiShMdEy_xdWUd2Y4aE2rD0wL5Wh8A19HSg66mWAWC5yb7pRyFHXixIB7R6Po2qJjbqNjZRAOffpLUa4YFh0nNhNDrjuxgmHLrF_xMZdMEOdR-KDyWSzLuSjn8hkNmqnL" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="212" data-original-width="1810" height="74" src="https://blogger.googleusercontent.com/img/a/AVvXsEiIHnD4qggzpVbVIr4pAPqfZ_Jgeu9SkG1YTd8iPFrl1qZglVBcIM8Veb0OY6HsiShMdEy_xdWUd2Y4aE2rD0wL5Wh8A19HSg66mWAWC5yb7pRyFHXixIB7R6Po2qJjbqNjZRAOffpLUa4YFh0nNhNDrjuxgmHLrF_xMZdMEOdR-KDyWSzLuSjn8hkNmqnL=w640-h74" width="640" /></a></div><div><br /></div>Fill out the form parameters. I entered the data shown below.<br /><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiw1hOr8B3fSjZPMs435BDzakosHqP8HvhVnrLq7yVe1Qc8BuF8k391hDt_rno07b12we3682ESr4_e-0Qfmy7tkpJfv9qq1lNYkF25EipYkrPQv8JG6SBXZxYZlhcEbR371pMbUvSvVRXDG3j3R4tynkKa-3ccbhT8fDiWXXBwaouUv4bNBAmHMyKbIWC9" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1338" data-original-width="1114" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEiw1hOr8B3fSjZPMs435BDzakosHqP8HvhVnrLq7yVe1Qc8BuF8k391hDt_rno07b12we3682ESr4_e-0Qfmy7tkpJfv9qq1lNYkF25EipYkrPQv8JG6SBXZxYZlhcEbR371pMbUvSvVRXDG3j3R4tynkKa-3ccbhT8fDiWXXBwaouUv4bNBAmHMyKbIWC9=w533-h640" width="533" /></a></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div class="separator" style="clear: both; text-align: center;"><br /></div></blockquote><div>Click on the blue <i>Next</i> button at the bottom of the page. Accept default values on the <i>Network</i> tab then click on blue <i>Next</i> button.<br /><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiqOsyUwg-3_8-g_4ATV_w4a6GNg-QyqRWb95FBcCBUmdK8zjK8CNvGaW4v343js0C9qpScCqO5rOmv49WXWP_3uvtjhRIDRgVgSPy6zFz54PnFcD0f0vTq-pETCKnCBlxWNFpOebtksv50IUB-cxJQInZ765OMhq6y_sd_fS008k8vzMrFzgROB2DSRhNH" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1344" data-original-width="1098" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEiqOsyUwg-3_8-g_4ATV_w4a6GNg-QyqRWb95FBcCBUmdK8zjK8CNvGaW4v343js0C9qpScCqO5rOmv49WXWP_3uvtjhRIDRgVgSPy6zFz54PnFcD0f0vTq-pETCKnCBlxWNFpOebtksv50IUB-cxJQInZ765OMhq6y_sd_fS008k8vzMrFzgROB2DSRhNH=w523-h640" width="523" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Also, accept default values on the <i>Tags</i> tab then click on blue <i>Next</i> button.</div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhTiq96044ojPpfO68U7CYOeROU0RShyIX_6mg4OpGukMnOtVCuw_qbwsxz5QGI2cmpVmcVwwFQhdd1V8E_uQjzsZAel3wXsNxeYEudMnlfgpO3z4tSzztJs-bCrz24oUgMzeYE-i4IxBP48Ub0fJV1fBIYDc-lamDufEucqCy7gOfUanwFCJiM0_kRcVE2" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1346" data-original-width="1058" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEhTiq96044ojPpfO68U7CYOeROU0RShyIX_6mg4OpGukMnOtVCuw_qbwsxz5QGI2cmpVmcVwwFQhdd1V8E_uQjzsZAel3wXsNxeYEudMnlfgpO3z4tSzztJs-bCrz24oUgMzeYE-i4IxBP48Ub0fJV1fBIYDc-lamDufEucqCy7gOfUanwFCJiM0_kRcVE2=w504-h640" width="504" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Click on the blue <i>Create</i> button when it appears at the bottom of tthe page.</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi0wUspIYfAB0zhWMsXqebwqKBTb1XLZyNj-cTXjzLXbNVBp7LCXsZigOx_N3pb75RAYjq51Dq0aVDTSzpfcyZm1L9yNdMa5shz3E24GvbhJkf3sZy1PKaJUm6ugUsh7mSp7VoVJiH57FglgFDqDMjANh4bzFCyXwLPWjv7REX5N8Tp02DgFEkFgq0BeiYp" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1328" data-original-width="1076" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEi0wUspIYfAB0zhWMsXqebwqKBTb1XLZyNj-cTXjzLXbNVBp7LCXsZigOx_N3pb75RAYjq51Dq0aVDTSzpfcyZm1L9yNdMa5shz3E24GvbhJkf3sZy1PKaJUm6ugUsh7mSp7VoVJiH57FglgFDqDMjANh4bzFCyXwLPWjv7REX5N8Tp02DgFEkFgq0BeiYp=w517-h640" width="517" /></a></div><br />Deployment takes some time. When it is complete, cclik on the blue "Go to resource" button.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjUgWajEDo7vg5KOh2wqSDyCihxi1WpXGwJSuLSeSrpcBBuKW5PuRCY8T9tFJ4jHas1YhVelv-vHoTuOAhfJInHe4a5f_EhrubFCIPkDE8wt6LDf5B8mvyeWEUxZpMT23-SYfiQtREFgv_WP-xRM8-wngGSTuTbMdPLYERjq1QQDUigZ2jjwtdVQdCXKpyp" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="998" data-original-width="2346" height="272" src="https://blogger.googleusercontent.com/img/a/AVvXsEjUgWajEDo7vg5KOh2wqSDyCihxi1WpXGwJSuLSeSrpcBBuKW5PuRCY8T9tFJ4jHas1YhVelv-vHoTuOAhfJInHe4a5f_EhrubFCIPkDE8wt6LDf5B8mvyeWEUxZpMT23-SYfiQtREFgv_WP-xRM8-wngGSTuTbMdPLYERjq1QQDUigZ2jjwtdVQdCXKpyp=w640-h272" width="640" /></a></div><br />Click on the <i>Explore</i> button.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhAwYmChaW78LnDWDoLG9xFZAgqAz8cyfB9--IzBq6scwdbbFum8NHqPWUqYFwg6SM-1HebafDrJpdrv8H0LtPqp2IYv_8r-8Y093BCGEvcbL2Br1mluu0C2Pe4Ke4qNUuKLllgvOEJkqE9FA-bUMW-TFXfh4VKx_b2HydCMGYEOKS73PfSUZEU3r7KUN2Y" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1040" data-original-width="2464" height="270" src="https://blogger.googleusercontent.com/img/a/AVvXsEhAwYmChaW78LnDWDoLG9xFZAgqAz8cyfB9--IzBq6scwdbbFum8NHqPWUqYFwg6SM-1HebafDrJpdrv8H0LtPqp2IYv_8r-8Y093BCGEvcbL2Br1mluu0C2Pe4Ke4qNUuKLllgvOEJkqE9FA-bUMW-TFXfh4VKx_b2HydCMGYEOKS73PfSUZEU3r7KUN2Y=w640-h270" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Click on "Bring your own data".</div><br /><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiAkSGUUC-S1kRKPsPKb9DPLnKngKipC5YTgHG-a_KBQ2zoAUADqJ174jc-W6f46jv5j4oj9uH_0dB6nm17p4u8QLMxY7KrGh7iHLb8E6EGq0Y0SF_t9sqX61ngwHxSt9S9HDW1W25k-39NjLNrPLSpRAlGcANL032sx57AhHJwM3dWibH2awsyWsGx8cOa" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="780" data-original-width="2314" height="216" src="https://blogger.googleusercontent.com/img/a/AVvXsEiAkSGUUC-S1kRKPsPKb9DPLnKngKipC5YTgHG-a_KBQ2zoAUADqJ174jc-W6f46jv5j4oj9uH_0dB6nm17p4u8QLMxY7KrGh7iHLb8E6EGq0Y0SF_t9sqX61ngwHxSt9S9HDW1W25k-39NjLNrPLSpRAlGcANL032sx57AhHJwM3dWibH2awsyWsGx8cOa=w640-h216" width="640" /></a></div><br /></div><div class="separator" style="clear: both; text-align: left;">We will need to create a deployment. Click on "Create new deployment".</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgrwGXvJh8EMTUK54Enk77E3pIJs9er-GuPkwL99w65x7RvKKrhECfge_fG_sQ-9UNyC5aT7Wocx2VuYPgioeAw7fBw4NVF-XeYzNZw8H9oUS0xLVAppboKb8ZwGb7CeKucXjFjB_k75l9-HXRXwY-yFpke9HBgyPSytpvvebV40CanmME9tjZ6dIGSF34g" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1150" data-original-width="2504" height="294" src="https://blogger.googleusercontent.com/img/a/AVvXsEgrwGXvJh8EMTUK54Enk77E3pIJs9er-GuPkwL99w65x7RvKKrhECfge_fG_sQ-9UNyC5aT7Wocx2VuYPgioeAw7fBw4NVF-XeYzNZw8H9oUS0xLVAppboKb8ZwGb7CeKucXjFjB_k75l9-HXRXwY-yFpke9HBgyPSytpvvebV40CanmME9tjZ6dIGSF34g=w640-h294" width="640" /></a></div><div><br /></div><div>Expand "Advanced options. Choose the <i>gpt-35-turbo </i>model, give the deployment a name (gpt-35-turbo-deploy), leave <i>Default</i> for "Content Filter". Click on the <i>Create</i> button.</div><br /><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjFNRlNLe1m1Bvn-LBUJ7R2KrkFNPo4ybQZiY9abczRy9reS2Wf3TiC8cOPMxT2-ZgVttE7nogcF2r_PGpes4bSz4yK3C65YbWwLr321Xx0-oTq_bxURJrDPYYgTnVto5kWD0KwmGIoOZjALY2np1WPMMRZcXYPR5QcWgDEdHyqC-AKOkxru0R1FZZ7xtn5" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1238" data-original-width="1142" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEjFNRlNLe1m1Bvn-LBUJ7R2KrkFNPo4ybQZiY9abczRy9reS2Wf3TiC8cOPMxT2-ZgVttE7nogcF2r_PGpes4bSz4yK3C65YbWwLr321Xx0-oTq_bxURJrDPYYgTnVto5kWD0KwmGIoOZjALY2np1WPMMRZcXYPR5QcWgDEdHyqC-AKOkxru0R1FZZ7xtn5=w368-h400" width="368" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div style="text-align: left;">On the "Data source" tab, choose as follows:</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>Select data source: </b>Azure Cognitive Search<br /><b>Subscription:</b> {your azure subscription}</div><div style="text-align: left;"><b>Azure Cognitive Search service: </b>{the cognitive service you created earlier}</div><div style="text-align: left;"><b>Azure Cognitive Search Index:</b> {the cognitive search index that was created earlier.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Enable "I acknowledge that connecting to an Azure Cognitive Search account will incue usage too my account", then click on <i>Next</i>.</div></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgsucddbQD8Xt_29wCvJNd9N_NCI3w-D5B9I1wz61Dj6aXESVLiiB9Rb-HomYDrs9xlL-jgYcN3nC6OSE8BBLb1kBlj32v-chYIEsIgIJ_hFdaFSzIbl_KkXaXWXkZM8sfukqYJruO_UkZGBdoNP4jAtNvfB7_PUpsHTnt5WPM28lHcxXphxVnMCwLVgqa6" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1642" data-original-width="1970" height="533" src="https://blogger.googleusercontent.com/img/a/AVvXsEgsucddbQD8Xt_29wCvJNd9N_NCI3w-D5B9I1wz61Dj6aXESVLiiB9Rb-HomYDrs9xlL-jgYcN3nC6OSE8BBLb1kBlj32v-chYIEsIgIJ_hFdaFSzIbl_KkXaXWXkZM8sfukqYJruO_UkZGBdoNP4jAtNvfB7_PUpsHTnt5WPM28lHcxXphxVnMCwLVgqa6=w640-h533" width="640" /></a></div><br /></div><div>Choose all the fields for "Content data". </div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjVcdk456tiTDY7-IRiH4njynMmslGcDO3b2PRwmvynnEPvpA-rgJwmMQxKjgAQ_DFjv-bwH1Nor07nAdf9JufysBLJz-JvWx_lwlgUlU7CAKj3Hrsr8lm01rlBsM6mACbsD9VM-a3bjRcCDnM00zt0dDMt5ezLl5oYYs8FbjSqYgrFVNEzL9-qio2UkO-7" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1148" data-original-width="358" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEjVcdk456tiTDY7-IRiH4njynMmslGcDO3b2PRwmvynnEPvpA-rgJwmMQxKjgAQ_DFjv-bwH1Nor07nAdf9JufysBLJz-JvWx_lwlgUlU7CAKj3Hrsr8lm01rlBsM6mACbsD9VM-a3bjRcCDnM00zt0dDMt5ezLl5oYYs8FbjSqYgrFVNEzL9-qio2UkO-7=w125-h400" width="125" /></a></div><br /></div><div><br /></div><div>Choose description for "Field name" and <i>Title</i> fields then click on <i>Next</i>.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjC-7une-CXAz7na4VOYOjUV9Q3rsPlB2oABOwQqY2GABlpvr8-80AYZuJJAE4JrPi6l3gvuRyt0_5wOj35lmKncJFdad-PuukNI8SGSEStgPdQaQcYDMidV5DpaKrL_CsN0tBxWNKBlJM3oHU3_sXpja6dMx993xQ__lTyRzv7yrwA8kfTiNXzd9OtSBSd" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1636" data-original-width="1976" height="530" src="https://blogger.googleusercontent.com/img/a/AVvXsEjC-7une-CXAz7na4VOYOjUV9Q3rsPlB2oABOwQqY2GABlpvr8-80AYZuJJAE4JrPi6l3gvuRyt0_5wOj35lmKncJFdad-PuukNI8SGSEStgPdQaQcYDMidV5DpaKrL_CsN0tBxWNKBlJM3oHU3_sXpja6dMx993xQ__lTyRzv7yrwA8kfTiNXzd9OtSBSd=w640-h530" width="640" /></a></div><div style="text-align: left;"><br /></div></div>Chooose <i>Keyword</i> for "Search type", then click oon <i>Next</i>.<br /><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi4oDeRUlveyUx3x_1KUC1srfnXYl-Byrd_UXTudB3UCEwPoS5wjPNhQTFMPPOHg0O8qS2FkYwxvCyCyqlcPS8uhPRhIikHMMygb3BLsQwxsed8RXywKQqr7VSWMe3PHD2Fzevo3qaF0ht_RdR-SbG4oPfxU1ULg7CbCTBQ3txE1GT-LrfGfMGl2qtlpxPb" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1624" data-original-width="1960" height="530" src="https://blogger.googleusercontent.com/img/a/AVvXsEi4oDeRUlveyUx3x_1KUC1srfnXYl-Byrd_UXTudB3UCEwPoS5wjPNhQTFMPPOHg0O8qS2FkYwxvCyCyqlcPS8uhPRhIikHMMygb3BLsQwxsed8RXywKQqr7VSWMe3PHD2Fzevo3qaF0ht_RdR-SbG4oPfxU1ULg7CbCTBQ3txE1GT-LrfGfMGl2qtlpxPb=w640-h530" width="640" /></a></div><br /></div>Finally, click on the "Save and close" button on the "Review and finish" tab.<br /><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjHDHvoovOPYDIAW9p7vbttLNZcCX1dhc_lQuyeNcHWzT0UVCrETOApOuMPUsWFWzOD6HPAru4PQeSB5yGSBZ6ycXH9PkSlPxhfADVP4WbqFoM1yOXtY50_AkgDZXnvGoqijSzIlVXxpbEbdBsdKUssSS9gr-9tKjWIXnxm2DnF1KstUBIrwMSIAPm9TL2x" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1620" data-original-width="1952" height="531" src="https://blogger.googleusercontent.com/img/a/AVvXsEjHDHvoovOPYDIAW9p7vbttLNZcCX1dhc_lQuyeNcHWzT0UVCrETOApOuMPUsWFWzOD6HPAru4PQeSB5yGSBZ6ycXH9PkSlPxhfADVP4WbqFoM1yOXtY50_AkgDZXnvGoqijSzIlVXxpbEbdBsdKUssSS9gr-9tKjWIXnxm2DnF1KstUBIrwMSIAPm9TL2x=w640-h531" width="640" /></a></div><br /><div style="text-align: left;">On the "Chat playground" page, the data source is automatically connected to the chat session.</div></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgYa1xNAArS6SexsN4hn-LV3qxno_GvRWDUiQapDy0JYIni0wzzubPWKdDFCSn_kirphputO8VJl6D6721ZTD8Alfn02y11gMjp4R5vDQFH1eHeZmrM4aA8wd-phnUWP0-prxPceHA0WNUz45Yglotl6C-aWQotr2ybKrZyBFb6fm0NnZDDwTFfsTfNaqIZ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="802" data-original-width="960" height="334" src="https://blogger.googleusercontent.com/img/a/AVvXsEgYa1xNAArS6SexsN4hn-LV3qxno_GvRWDUiQapDy0JYIni0wzzubPWKdDFCSn_kirphputO8VJl6D6721ZTD8Alfn02y11gMjp4R5vDQFH1eHeZmrM4aA8wd-phnUWP0-prxPceHA0WNUz45Yglotl6C-aWQotr2ybKrZyBFb6fm0NnZDDwTFfsTfNaqIZ=w400-h334" width="400" /></a></div></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Enter the following in the chat field: <i>property in renton</i>. Then, hit ENTER on your keyboard.</div><div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj7oA9XaIBOlkZY-eur7_xroOLgW62WKUzIIbxK4j415wtjmCvxoJzDxOfxdEIpPJzr3N8KyYZGj2R9z-_PWRIV0i1dpGmqTD0hkSxhHj0oFDxyEw_iItl_kkrTVXW16nk3gnpwXdMwMOxXBn6NHZ6mrViYyOeM64YuuQyxB1fVrEkpDBeBepq61-KUZfe3" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1278" data-original-width="1084" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEj7oA9XaIBOlkZY-eur7_xroOLgW62WKUzIIbxK4j415wtjmCvxoJzDxOfxdEIpPJzr3N8KyYZGj2R9z-_PWRIV0i1dpGmqTD0hkSxhHj0oFDxyEw_iItl_kkrTVXW16nk3gnpwXdMwMOxXBn6NHZ6mrViYyOeM64YuuQyxB1fVrEkpDBeBepq61-KUZfe3=w340-h400" width="340" /></a></div><br /><div style="text-align: left;">A response similar to the following will appear:</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi61rs60Q7qA7_G1hExOFCUDcvTGZ0BavUqFy6yPRsMMs3R0YTznvl_tBBmb-aE3xNhnofk21Ue8x0030tG1N3zKZyUZv1Uav6uVhb4nE5dVBEIdxVDJWoj39ye5JaSEUsZHYIxB0CJZDzfwwhuJm0_ztDfv9zgp1aZ-D_2dVJZn9a49RyHU3BV8_PBHkUg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="1060" data-original-width="1104" height="384" src="https://blogger.googleusercontent.com/img/a/AVvXsEi61rs60Q7qA7_G1hExOFCUDcvTGZ0BavUqFy6yPRsMMs3R0YTznvl_tBBmb-aE3xNhnofk21Ue8x0030tG1N3zKZyUZv1Uav6uVhb4nE5dVBEIdxVDJWoj39ye5JaSEUsZHYIxB0CJZDzfwwhuJm0_ztDfv9zgp1aZ-D_2dVJZn9a49RyHU3BV8_PBHkUg=w400-h384" width="400" /></a></div><br /><h3 style="text-align: left;">Conclusion</h3><div style="text-align: left;">We were able to link Azure OpenAI with custom data and generate output through ChatGPT chat. The data source, of course, can be any enterprise relational data.</div></div><br /></div></div></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-26983376850550500172023-09-18T11:54:00.003-07:002023-09-19T21:18:45.665-07:00Reading appsettings.json from a C# static method<h2 style="text-align: left;"><b>Overview</b></h2><p>In this tutorial I will demonstrate an easy way to read configuration settings in appsettings.json from a static C# method. This technique comes in handy when you are building a non-traditional C# application where you cannot use dependency injection to access the configuration object. I will demonstrate my solution with a simple C# console application.</p><h3 style="text-align: left;">Getting Started</h3><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">dotnet new console -o ConfigDemo</span></p><p><span style="font-family: courier;">cd ConfigDemo</span></p></blockquote><p><br /></p><p>We need to install a package to help us read JSON based configuration files:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"><span style="font-family: courier;">dotnet add package Microsoft.Extensions.Configuration.Json</span></p></blockquote><p>In the root folder of your application, create a file name <i>appsettings.json</i> with the following content that specifies a database connection string:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">{</span></p><p><span style="font-family: courier;"> "ConnectionStrings": {</span></p><p><span style="font-family: courier;"> "DefaultConnection": "DataSource=foo.db;Cache=Shared;"</span></p><p><span style="font-family: courier;"> }</span></p><p><span style="font-family: courier;">}</span></p></blockquote><p>When our application gets built and packaged, we want this file to get copied to the output directory. Therefore, we need to add the following XML to the <i>ConfigDemo.csproj </i>file just before the closing <i></Project></i> tag.</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;"><ItemGroup></span></p><p><span style="font-family: courier;"> <None Include="*.json" CopyToOutputDirectory="PreserveNewest" /></span></p><p><span style="font-family: courier;"></ItemGroup> </span></p></blockquote><h3 style="text-align: left;">The Code</h3><p>Let us create a helper class with a static method named <i>GetConfigValue()</i> that reads from the <i>appsettings.json </i>file. Create a C# class named <i>Utils.cs</i> and add to it the following code:</p><p><span style="font-family: courier;">public class Utils {</span></p><p><span style="font-family: courier;"> public static string GetConfigValue(string config) {</span></p><p><span style="font-family: courier;"> IConfigurationBuilder builder = new ConfigurationBuilder();</span></p><p><span style="font-family: courier;"><br /></span></p><p><span style="font-family: courier;"> if (System.IO.File.Exists("appsettings.json"))</span></p><p><span style="font-family: courier;"> builder.AddJsonFile("appsettings.json", false, true);</span></p><p><span style="font-family: courier;"><br /></span></p><p><span style="font-family: courier;"> if (System.IO.File.Exists("appsettings.Development.json"))</span></p><p><span style="font-family: courier;"> builder.AddJsonFile("appsettings.Development.json", false, true);</span></p><p><span style="font-family: courier;"><br /></span></p><p><span style="font-family: courier;"> IConfigurationRoot root = builder.Build();</span></p><p><span style="font-family: courier;"> return root[config]!;</span></p><p><span style="font-family: courier;"> }</span></p><p><span style="font-family: courier;"><br /></span></p><p><span style="font-family: courier;">}</span></p><p>The above code first checks <i>appsettings.json</i> for a configuration setting. If it does not find it there then it looks into <i>appsettings.Development.json</i>.</p><p>Note that you need to add the following <i>using</i> statement at the top of <i>Utils.cs</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"><span style="font-family: courier;">using Microsoft.Extensions.Configuration;</span></p></blockquote><h3 style="text-align: left;">Using our static method</h3><p>Replace the code in <i>Program.cs</i> with the following:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">var connStr = Utils.GetConfigValue("ConnectionStrings:DefaultConnection");</span></p><p><span style="font-family: courier;">Console.WriteLine($"Connection string: {connStr}");</span></p></blockquote><p>Run the application. The output should look like this:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><p style="text-align: left;"><span style="font-family: courier;">Connection string: DataSource=foo.db;Cache=Shared;</span></p></blockquote><p>Placing database connection strings in <i>appsettings.json</i> is not a good idea. It is best to save it in <i>appsettings.Development.json</i> while making sure that the latter is in your <i>.gitignore</i> so it does not get pushed into source control.</p><p>Copy <i>appsettings.json</i> to <i>appsettings.Development.json</i>. Thereafter, delete the following from <i>appsettings.json</i>:</p><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><p><span style="font-family: courier;">"ConnectionStrings": {</span></p><p><span style="font-family: courier;"> "DefaultConnection": "DataSource=foo.db;Cache=Shared;"</span></p><p><span style="font-family: courier;">}</span></p></blockquote><p>Run the application again. You should get the same results with the connection string being read from <i>appsettings.Development.json</i> instead of <i>appsettings.json.</i></p><p>I hope this helps in making you an even better C# developer.</p><div><br /></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-46523699398077318132023-06-10T12:23:00.003-07:002023-06-10T16:14:54.688-07:00Using ChatGPT Chat Completion API with ASP.NET Blazor Web Assembly<p>In this tutorial you will create a very simple Blazor Web Assembly application that interacts with ChatGPT.</p><h2 style="text-align: left;">Prerequisites</h2><p>You will need the following:</p><p></p><ul style="text-align: left;"><li>.NET 7.0 or higher</li><li>Visual Studio Code</li><li>A ChatGPT account with https://openai.com</li></ul><p></p><h2>Get an ChatGPT API key from openai.com</h2><p>Visit <a href="https://openai.com">https://openai.com</a> and create an account. The login page looks like this:</p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgu9x4DCuqggTLjHfjk1gIeaArDLHcPbmZZtHp3g1vhQPiXia2Hf9OBEYnQH5ILWcjpP56DDpeYh06nIblRcI-NDCYj70EzsqmS8hiC2ohvDbR3nJunyUJFB_S4RpqRX_RlHcxWH24A-xesm1kOUCGtucVCn79CfkhJTMAlJzLRXDNoJ9nHU-jxbSg-hA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="344" data-original-width="741" height="298" src="https://blogger.googleusercontent.com/img/a/AVvXsEgu9x4DCuqggTLjHfjk1gIeaArDLHcPbmZZtHp3g1vhQPiXia2Hf9OBEYnQH5ILWcjpP56DDpeYh06nIblRcI-NDCYj70EzsqmS8hiC2ohvDbR3nJunyUJFB_S4RpqRX_RlHcxWH24A-xesm1kOUCGtucVCn79CfkhJTMAlJzLRXDNoJ9nHU-jxbSg-hA=w640-h298" width="640" /></a></div><br />Click on your profile and select “View API keys”. On the next page, click on the “+ Create new secret key” button.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEha-5U9kNgySI6DjeKgkkWPJMBw9_DFZWo0T3xgO6p4LhG0FvivBRM9tbywDM9Y9CVWy6V3Vizoy7rp_ic2SNwXdqWJeU15bZz3Yc8jcxl_gO-reCej_R_ia8AXvtd8b2M_UzMc1QC8nRvvkAFrcZRFG6HOQ905fnX3iN0qdxIsAUHMCpoXJcXSRMjJ-g" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="351" data-original-width="785" height="286" src="https://blogger.googleusercontent.com/img/a/AVvXsEha-5U9kNgySI6DjeKgkkWPJMBw9_DFZWo0T3xgO6p4LhG0FvivBRM9tbywDM9Y9CVWy6V3Vizoy7rp_ic2SNwXdqWJeU15bZz3Yc8jcxl_gO-reCej_R_ia8AXvtd8b2M_UzMc1QC8nRvvkAFrcZRFG6HOQ905fnX3iN0qdxIsAUHMCpoXJcXSRMjJ-g=w640-h286" width="640" /></a></div><br />Give your key a name, then click on “Generate secret key”.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhltdSw3krJbl4-6H8zD-2RCQeEo0Ib0TWs-GzEjHK7MfI2ZNkj7VxT23VhLFtUdwWUnLMhCqFaKdVcIt95FJm4XmYnjbJ_32sak_Aq8b_PHETccOH-ofpYe7IfS6ED4Na_MdYseEwgG3QyS9GrkU8vDJdZwtEcPv2w7BRSZHLvHfUwLsJ7c2HH7UnEYg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="313" data-original-width="763" height="262" src="https://blogger.googleusercontent.com/img/a/AVvXsEhltdSw3krJbl4-6H8zD-2RCQeEo0Ib0TWs-GzEjHK7MfI2ZNkj7VxT23VhLFtUdwWUnLMhCqFaKdVcIt95FJm4XmYnjbJ_32sak_Aq8b_PHETccOH-ofpYe7IfS6ED4Na_MdYseEwgG3QyS9GrkU8vDJdZwtEcPv2w7BRSZHLvHfUwLsJ7c2HH7UnEYg=w640-h262" width="640" /></a></div><br />An API key is generated. You must save this key somewhere because this is your only chance to view it as it cannot be viewed again. Click on the copy button and paste it is a safe place.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgkQ4uwymBcKsioube9-Jnql0Jr4vwDf1oeiqvh3iRnx7rbyTz06Pyk_E9odUOyUogAjyymvhjyJ75n_MxOstkkcqcfyvySUytA1Wq35s1NcDlgcGb0t-gnnejj0aM6V7SSf-Hpu-xawufd8zHUuVd94FQvphbm4bi7XXRgT181SALhEvATngt_9LlYvQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="426" data-original-width="849" height="201" src="https://blogger.googleusercontent.com/img/a/AVvXsEgkQ4uwymBcKsioube9-Jnql0Jr4vwDf1oeiqvh3iRnx7rbyTz06Pyk_E9odUOyUogAjyymvhjyJ75n_MxOstkkcqcfyvySUytA1Wq35s1NcDlgcGb0t-gnnejj0aM6V7SSf-Hpu-xawufd8zHUuVd94FQvphbm4bi7XXRgT181SALhEvATngt_9LlYvQ=w400-h201" width="400" /></a></div><h2>Creating ASP.NET Blazor Web Assembly App</h2><div>We will create a very simple <i>Chat Completion</i> client-side blazor app that discusses topics relating to the National Basketball Association (NBA).</div><div><br /></div><div>In a working folder, execute the following terminal window commands to create an ASP.NET Blazor Web Assembly application named <i>ChatGPTBlazorWasm</i>:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">dotnet new blazorwasm -n ChatGPTBlazorWasm</span></div></div><div><div><span style="font-family: courier;">cd ChatGPTBlazorWasm</span></div></div></blockquote><div><div><br /></div><div>Open the blazor application in VS Code with:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">code .</span></div><div style="text-align: center;"><span style="font-family: courier;"><br /></span></div><div>Replace contents of<i> Pages/Index.razor</i> with the following code:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier;">@page "/"</span></div><div><span style="font-family: courier;">@inject HttpClient httpClient</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><h1>ChatGPT with Blazor WebAssembly (NBA)</h1></span></div><div><span style="font-family: courier;"><textarea @bind="dialog" cols="100" rows="15"></textarea></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><button @onclick="GetOpenAIChatCompletions" class="btn btn-success">Call Completion</button></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">@code {</span></div><div><span style="font-family: courier;"> private string? dialog;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> protected override void OnInitialized()</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> const string? apiKey = "fake-chatgpt-api-key";</span></div><div><span style="font-family: courier;"> httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");</span></div><div><span style="font-family: courier;"><br /></span></div><div><div><span style="font-family: courier;"> Message[] messages = new Message[] {</span></div><div><span style="font-family: courier;"> new Message { Role = "system", Content = "You are a helpful assistant." },</span></div><div><span style="font-family: courier;"> new Message { Role = "user", Content = "Who won the NBA in 2020?" },</span></div><div><span style="font-family: courier;"> new Message { Role = "assistant", Content = "The Los Angeles Lakers won the NBA in 2020." },</span></div><div><span style="font-family: courier;"> new Message { Role = "user", Content = "Where was it played?" },</span></div><div><span style="font-family: courier;"> };</span></div></div><div><br /></div><div><span style="font-family: courier;"> dialog = "";</span></div><div><span style="font-family: courier;"> foreach (var message in messages)</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> dialog += $"role: {message.Role}\ncontent: {message.Content}\n\n";</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> private async Task GetOpenAIChatCompletions()</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> string? text = this.dialog;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> // remove any * in text</span></div><div><span style="font-family: courier;"> text = text!.Replace("*", "");</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var lines = text.Split('\n');</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> // delete empty items in lines array</span></div><div><span style="font-family: courier;"> for (var i = 0; i < lines.Length; i++)</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> if (lines[i] == "")</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> lines = lines.Take(i).Concat(lines.Skip(i + 1)).ToArray();</span></div><div><span style="font-family: courier;"> i--;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var result = new List<Message>();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> for (var i = 0; i < lines.Length; i += 2)</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> if (lines[i] == "")</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> continue;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> var r = lines[i].Split(": ")[1];</span></div><div><span style="font-family: courier;"> var c = lines[i + 1].Split(": ")[1];</span></div><div><span style="font-family: courier;"> result.Add(new Message { Role = r, Content = c });</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var response = await httpClient.PostAsJsonAsync("https://api.openai.com/v1/chat/completions", new</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> max_tokens = 50,</span></div><div><span style="font-family: courier;"> n = 1,</span></div><div><span style="font-family: courier;"> stop = "\n",</span></div><div><span style="font-family: courier;"> model = "gpt-3.5-turbo",</span></div><div><span style="font-family: courier;"> temperature = 0.5,</span></div><div><span style="font-family: courier;"> messages = result</span></div><div><span style="font-family: courier;"> });</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var data = await response.Content.ReadFromJsonAsync<OpenAIResponse>();</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> Message? message = data!.choices![0].message;</span></div><div><span style="font-family: courier;"> var content = message!.Content;</span></div><div><span style="font-family: courier;"> var role = message!.Role;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> var reply = $"role: {role}\ncontent: {content}";</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> this.dialog += "*" + reply + "*" + Environment.NewLine + Environment.NewLine;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> private class Message</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> public string? Role { get; set; }</span></div><div><span style="font-family: courier;"> public string? Content { get; set; }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> private class OpenAIResponse</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> public Choice[]? choices { get; set; }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> private class Choice</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> public Message? message { get; set; }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div></blockquote><div><h4 style="text-align: left;">Explaining the above code:</h4></div><div><ul style="text-align: left;"><li>The UI consists of a textarea and button.</li><li><i>OnInitialized() </i>method</li><ul><li>remember to set the value of apiKey with the key that you obtained from https://openai.com</li><li>an HTTP header with <i>Authorization </i>key is created with the appropriate <i>Bearer </i>value</li><li>array named <i>messages </i>is declared with the initial dialog text</li><li>the <i>messages </i>array is used to set the contents of the textarea</li></ul><li><i>GetOpenAIChatCompletions() </i>method</li><ul><li>this method is called when the button is clicked</li><li>a POST request is made to the endpoint at <a href="https://api.openai.com/v1/chat/completions">https://api.openai.com/v1/chat/completions</a></li><li>response from the server is appended to the <i>textarea</i>. In order to distinguish the response from the rest of the dialog, it is surrounded by *</li></ul></ul></div><div>After replacing the value of <i>apiKey </i>in the <i>onInitialized()</i> method, run the application by executing the following command i a terminal window:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></div><div><br /></div><div>You should see the following page:</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgBmFZObiFudXEeLrVq1Wp-wh12spewygnGdeFJzXGjgfoPAC_Vl6yUSnR8esXNhzDFQUZtZNTVwqzjyw_89fXvaRnaoE2amu_FIJf6kUOuT0oWNFgUSTM8o5_JsMy6VpMl181mNc7tWLOr6tk6KtwQPkdVdl0_AbhZ_mBFnnBNlbQS0ynuEcjqb9aEKw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="746" data-original-width="1390" height="344" src="https://blogger.googleusercontent.com/img/a/AVvXsEgBmFZObiFudXEeLrVq1Wp-wh12spewygnGdeFJzXGjgfoPAC_Vl6yUSnR8esXNhzDFQUZtZNTVwqzjyw_89fXvaRnaoE2amu_FIJf6kUOuT0oWNFgUSTM8o5_JsMy6VpMl181mNc7tWLOr6tk6KtwQPkdVdl0_AbhZ_mBFnnBNlbQS0ynuEcjqb9aEKw=w640-h344" width="640" /></a></div><br /></div><div>Note the text grounded in the NBA context about where the 2020 finals were played. Click on the green "Call Completion" button. The response indicates the the finals were held during the COVID pandemic in Orlando.</div><div> <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEieau7IBpOYwnb9qbseVZzBW1ioO68lCSr642BKKRdjAxH3iwlEYMK0k8FX3LrLq8XfsRvOvlTkCFxRMVr9yxFpSug5m91o6S7FAar7OZM2UP6MBDYeruOAaLhf6uEnDJd3B04EDnvq3VfTwtJZuXjo2SAlwG4fw_4yq3rJjdenyx-Ps2FmgaGkgazMSA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="737" data-original-width="1393" height="338" src="https://blogger.googleusercontent.com/img/a/AVvXsEieau7IBpOYwnb9qbseVZzBW1ioO68lCSr642BKKRdjAxH3iwlEYMK0k8FX3LrLq8XfsRvOvlTkCFxRMVr9yxFpSug5m91o6S7FAar7OZM2UP6MBDYeruOAaLhf6uEnDJd3B04EDnvq3VfTwtJZuXjo2SAlwG4fw_4yq3rJjdenyx-Ps2FmgaGkgazMSA=w640-h338" width="640" /></a></div></div><div><br /></div><div>Let’s ask about what happened a year earlier in 2019 by appending this additional text:</div></div><div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><span style="font-family: courier;">role: user</span></div><div><span style="font-family: courier;">content: How about in 2019? Which team won and where was it played?</span></div></blockquote><div><div><br /></div><div>Click on the “Call Completion” button. Our page now looks like this:</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh4HlVFSmMRM1sDtvCnUFw62hDy8rV3g4Sp_aBZ9YKpBcfUnll8AQhHezrlUo84EhZcV0MWIXOqF-Q_wc6GdS34gdX7Oov5NF5EOdhPd4xc92wRsTT5sALPscr3BNnbUvfMae28YJkylhOj8H4MdUY0mhNnJcwnLtRZuPil705y6nteoM-hQZG3wuTrnQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="686" data-original-width="1377" height="318" src="https://blogger.googleusercontent.com/img/a/AVvXsEh4HlVFSmMRM1sDtvCnUFw62hDy8rV3g4Sp_aBZ9YKpBcfUnll8AQhHezrlUo84EhZcV0MWIXOqF-Q_wc6GdS34gdX7Oov5NF5EOdhPd4xc92wRsTT5sALPscr3BNnbUvfMae28YJkylhOj8H4MdUY0mhNnJcwnLtRZuPil705y6nteoM-hQZG3wuTrnQ=w640-h318" width="640" /></a></div></div></div></div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div><div>We are told that the 2019 NBA champions were the Toronto Raptors.</div><div><br /></div><div>You can see from this very simple tutorial that it is quite easy to incorporate ChaGPT into your Blazor Web Assembly applications.</div><div><br /></div></div><div><br /></div><div><br /></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-61906225884857929142023-06-09T16:22:00.005-07:002023-06-10T16:15:25.867-07:00Using ChatGPT Chat Completion API with pure JavaScript<p> In this tutorial you will create a very simple JavaScript web page that interacts with ChatGPT.</p><h2 style="text-align: left;">Get an API key from openai.com</h2><p>Visit <a href="https://openai.com">https://openai.com</a> and create an account. The login page looks like this:</p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgu9x4DCuqggTLjHfjk1gIeaArDLHcPbmZZtHp3g1vhQPiXia2Hf9OBEYnQH5ILWcjpP56DDpeYh06nIblRcI-NDCYj70EzsqmS8hiC2ohvDbR3nJunyUJFB_S4RpqRX_RlHcxWH24A-xesm1kOUCGtucVCn79CfkhJTMAlJzLRXDNoJ9nHU-jxbSg-hA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="344" data-original-width="741" height="298" src="https://blogger.googleusercontent.com/img/a/AVvXsEgu9x4DCuqggTLjHfjk1gIeaArDLHcPbmZZtHp3g1vhQPiXia2Hf9OBEYnQH5ILWcjpP56DDpeYh06nIblRcI-NDCYj70EzsqmS8hiC2ohvDbR3nJunyUJFB_S4RpqRX_RlHcxWH24A-xesm1kOUCGtucVCn79CfkhJTMAlJzLRXDNoJ9nHU-jxbSg-hA=w640-h298" width="640" /></a></div><br />Click on your profile and select “View API keys”. On the next page, click on the “+ Create new secret key” button.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEha-5U9kNgySI6DjeKgkkWPJMBw9_DFZWo0T3xgO6p4LhG0FvivBRM9tbywDM9Y9CVWy6V3Vizoy7rp_ic2SNwXdqWJeU15bZz3Yc8jcxl_gO-reCej_R_ia8AXvtd8b2M_UzMc1QC8nRvvkAFrcZRFG6HOQ905fnX3iN0qdxIsAUHMCpoXJcXSRMjJ-g" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="351" data-original-width="785" height="286" src="https://blogger.googleusercontent.com/img/a/AVvXsEha-5U9kNgySI6DjeKgkkWPJMBw9_DFZWo0T3xgO6p4LhG0FvivBRM9tbywDM9Y9CVWy6V3Vizoy7rp_ic2SNwXdqWJeU15bZz3Yc8jcxl_gO-reCej_R_ia8AXvtd8b2M_UzMc1QC8nRvvkAFrcZRFG6HOQ905fnX3iN0qdxIsAUHMCpoXJcXSRMjJ-g=w640-h286" width="640" /></a></div><br />Give your key a name, then click on “Generate secret key”.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhltdSw3krJbl4-6H8zD-2RCQeEo0Ib0TWs-GzEjHK7MfI2ZNkj7VxT23VhLFtUdwWUnLMhCqFaKdVcIt95FJm4XmYnjbJ_32sak_Aq8b_PHETccOH-ofpYe7IfS6ED4Na_MdYseEwgG3QyS9GrkU8vDJdZwtEcPv2w7BRSZHLvHfUwLsJ7c2HH7UnEYg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="313" data-original-width="763" height="262" src="https://blogger.googleusercontent.com/img/a/AVvXsEhltdSw3krJbl4-6H8zD-2RCQeEo0Ib0TWs-GzEjHK7MfI2ZNkj7VxT23VhLFtUdwWUnLMhCqFaKdVcIt95FJm4XmYnjbJ_32sak_Aq8b_PHETccOH-ofpYe7IfS6ED4Na_MdYseEwgG3QyS9GrkU8vDJdZwtEcPv2w7BRSZHLvHfUwLsJ7c2HH7UnEYg=w640-h262" width="640" /></a></div><br />An API key is generated. You must save this key somewhere because this is your only chance to view it as it cannot be viewed again. Click on the copy button and paste it is a safe place.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgkQ4uwymBcKsioube9-Jnql0Jr4vwDf1oeiqvh3iRnx7rbyTz06Pyk_E9odUOyUogAjyymvhjyJ75n_MxOstkkcqcfyvySUytA1Wq35s1NcDlgcGb0t-gnnejj0aM6V7SSf-Hpu-xawufd8zHUuVd94FQvphbm4bi7XXRgT181SALhEvATngt_9LlYvQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="426" data-original-width="849" height="201" src="https://blogger.googleusercontent.com/img/a/AVvXsEgkQ4uwymBcKsioube9-Jnql0Jr4vwDf1oeiqvh3iRnx7rbyTz06Pyk_E9odUOyUogAjyymvhjyJ75n_MxOstkkcqcfyvySUytA1Wq35s1NcDlgcGb0t-gnnejj0aM6V7SSf-Hpu-xawufd8zHUuVd94FQvphbm4bi7XXRgT181SALhEvATngt_9LlYvQ=w400-h201" width="400" /></a></div><br /><h2 style="text-align: left;">Creating a simple JavaScript app</h2><div>We will create a very simple <i>Chat Completion</i> application that we can ask questions to and it will use the smarts of ChatGPT to provide us with answers. We will ground the chat so that it discusses topics related to the National Basketball Association (NBA).</div><div><br /></div><div>Create a folder named <i>ChatCompletionJS</i>. Inside of that folder, create an HTML file named <i>chatgpt-chat-completion_nba.html</i>. Using a text editor, add the following HTML code to <i>chatgpt-chat-completion_nba.html</i>:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;"><!DOCTYPE html></span></div></div><div><div><span style="font-family: courier;"><html lang="en"></span></div></div><div><div><span style="font-family: courier;"> <head></span></div></div><div><div><span style="font-family: courier;"> <title>ChatGPT with JavaScript (NBA)</title></span></div></div><div><div><span style="font-family: courier;"> </head></span></div></div><div><div><span style="font-family: courier;"> <body></span></div></div><div><div><span style="font-family: courier;"> <div></span></div></div><div><div><span style="font-family: courier;"> <h1>ChatGPT with JavaScript (NBA)</h1></span></div></div><div><div><span style="font-family: courier;"> <textarea id="dialog" cols="100" rows="30"></textarea></span></div></div><div><div><span style="font-family: courier;"> <br /></span></div></div><div><div><span style="font-family: courier;"> <button id="btn">Call Completion</button></span></div></div><div><div><span style="font-family: courier;"> </div></span></div></div><div><div><span style="font-family: courier;"> <script></span></div></div><div><div><span style="font-family: courier;"> </script></span></div></div><div><div><span style="font-family: courier;"> </body></span></div></div><div><div><span style="font-family: courier;"></html></span></div></div></blockquote><div><div><br /></div><div>The page will look like this when viewed in your favorite browser:</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjiBT4PRzfkhZbkxJP61QARmN0ujAJYeNP07JRfPvlRzmyN0edffwfEXdsZCdsqryzUHIyoWpddPQTHy2G8q5YvjjhYQsvaGFzVyHg2eAgmR2zVxGJXi1Bc11x5g8jTSifEl5RHbe1kv_jox0bfEdiGRF6fnaJ0Mz91krVuIeOft5M2uVNgaWcYovp4WQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="453" data-original-width="564" height="321" src="https://blogger.googleusercontent.com/img/a/AVvXsEjiBT4PRzfkhZbkxJP61QARmN0ujAJYeNP07JRfPvlRzmyN0edffwfEXdsZCdsqryzUHIyoWpddPQTHy2G8q5YvjjhYQsvaGFzVyHg2eAgmR2zVxGJXi1Bc11x5g8jTSifEl5RHbe1kv_jox0bfEdiGRF6fnaJ0Mz91krVuIeOft5M2uVNgaWcYovp4WQ=w400-h321" width="400" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><div>We have a simple <i>textarea </i>and a <i>button</i>. </div><div><br /></div><div>Let’s add some JavaScript code that will ground our discussion on the topic of the NBA. Add this code inside the <i><script> . . . </script> </i>tags:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">const messages = [</span></div></div><div><div><span style="font-family: courier;"> { role: "system", content: "You are a helpful assistant." },</span></div></div><div><div><span style="font-family: courier;"> { role: "user", content: "Who won the NBA in 2020?" },</span></div></div><div><div><span style="font-family: courier;"> {</span></div></div><div><div><span style="font-family: courier;"> role: "assistant",</span></div></div><div><div><span style="font-family: courier;"> content: "The Los Angeles Lakers won the NBA in 2020.",</span></div></div><div><div><span style="font-family: courier;"> },</span></div></div><div><div><span style="font-family: courier;"> { role: "user", content: "Where was it played?" },</span></div></div><div><div><span style="font-family: courier;">];</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;">let defaultMessage = "";</span></div></div><div><div><span style="font-family: courier;">messages.forEach((item) => {</span></div></div><div><div><span style="font-family: courier;"> defaultMessage += `role: ${item.role}\ncontent: ${item.content}\n\n`;</span></div></div><div><div><span style="font-family: courier;">});</span></div></div></blockquote><div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><div style="text-align: left;"><span style="font-family: courier;">document.getElementById("dialog").value = defaultMessage;</span></div></div></blockquote><div><div><br /></div><div>The above code basically takes the contents of the messages array and neatly places it inside the HTML <i>textarea</i>. Your page now looks like this:</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiFoA0QEHoGvFx8t3ZtqHz3CXLMUyhLqLTF8ZrvrSfgCOY7L3ZLDvYmQc0q3q1p8uYXOUZNLQ08fp9oyXZo8oUd083Sa5Hf4RULHKWsILZRipP05U92Zg7kbxeMXwjO2lueO1iMi30GkuVanSdGd_OIUGYyTexr0qKkdtjvrbrDI3-P8hfRaHtzC-hn9w" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="434" data-original-width="586" height="474" src="https://blogger.googleusercontent.com/img/a/AVvXsEiFoA0QEHoGvFx8t3ZtqHz3CXLMUyhLqLTF8ZrvrSfgCOY7L3ZLDvYmQc0q3q1p8uYXOUZNLQ08fp9oyXZo8oUd083Sa5Hf4RULHKWsILZRipP05U92Zg7kbxeMXwjO2lueO1iMi30GkuVanSdGd_OIUGYyTexr0qKkdtjvrbrDI3-P8hfRaHtzC-hn9w=w640-h474" width="640" /></a></div><br /><div>As you can see, we are asking ChatGPT to let us know where the NBA finals in 2020 were held. Let’s add some code to work with ChatGPT that sends data to it and obtains a reply. Add the following JavaScript <i>getOpenAIChatCompletions() </i>function right under the opening <i><script></i> tag:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">async function getOpenAIChatCompletions() {</span></div></div><div><div><span style="font-family: courier;"> const apiKey = "this-is-a-fake-chatgpt-api-key";</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> let text = document.getElementById("dialog").value;</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> // remove any * in text</span></div></div><div><div><span style="font-family: courier;"> text = text.replace(/\*/g, '');</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> const lines = text.split('\n');</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> // delete empty items in lines array</span></div></div><div><div><span style="font-family: courier;"> for (let i = 0; i < lines.length; i++) {</span></div></div><div><div><span style="font-family: courier;"> if (lines[i] === '') {</span></div></div><div><div><span style="font-family: courier;"> lines.splice(i, 1);</span></div></div><div><div><span style="font-family: courier;"> }</span></div></div><div><div><span style="font-family: courier;"> }</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> const result = [];</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> for (let i = 0; i < lines.length; i += 2) {</span></div></div><div><div><span style="font-family: courier;"> if (lines[i] === '') {</span></div></div><div><div><span style="font-family: courier;"> continue;</span></div></div><div><div><span style="font-family: courier;"> }</span></div></div><div><div><span style="font-family: courier;"> const role = lines[i].split(': ')[1];</span></div></div><div><div><span style="font-family: courier;"> const content = lines[i+1].split(': ')[1];</span></div></div><div><div><span style="font-family: courier;"> result.push({ role, content });</span></div></div><div><div><span style="font-family: courier;"> }</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> console.log(result);</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> fetch("https://api.openai.com/v1/chat/completions", {</span></div></div><div><div><span style="font-family: courier;"> method: "POST",</span></div></div><div><div><span style="font-family: courier;"> headers: {</span></div></div><div><div><span style="font-family: courier;"> "Content-Type": "application/json",</span></div></div><div><div><span style="font-family: courier;"> Authorization: `Bearer ${apiKey}`,</span></div></div><div><div><span style="font-family: courier;"> },</span></div></div><div><div><span style="font-family: courier;"> body: JSON.stringify({</span></div></div><div><div><span style="font-family: courier;"> max_tokens: 50,</span></div></div><div><div><span style="font-family: courier;"> n: 1,</span></div></div><div><div><span style="font-family: courier;"> stop: "\n",</span></div></div><div><div><span style="font-family: courier;"> model: "gpt-3.5-turbo",</span></div></div><div><div><span style="font-family: courier;"> temperature: 0.5,</span></div></div><div><div><span style="font-family: courier;"> messages: result,</span></div></div><div><div><span style="font-family: courier;"> }),</span></div></div><div><div><span style="font-family: courier;"> })</span></div></div><div><div><span style="font-family: courier;"> .then((response) => response.json())</span></div></div><div><div><span style="font-family: courier;"> .then((data) => {</span></div></div><div><div><span style="font-family: courier;"> const message = data.choices[0].message;</span></div></div><div><div><span style="font-family: courier;"> const content = message.content;</span></div></div><div><div><span style="font-family: courier;"> const role = message.role;</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> const dialog = `role: ${role}\ncontent: ${content}`;</span></div></div><div><div><span style="font-family: courier;"><br /></span></div></div><div><div><span style="font-family: courier;"> document.getElementById("dialog").value += "*" + dialog + "*";</span></div></div><div><div><span style="font-family: courier;"> });</span></div></div><div><div><span style="font-family: courier;">}</span></div></div></blockquote><div><div><br /></div><div>Remember to replace the value of <i>apiKey </i>with the API key that you copied from <a href="https://openai.com">https://openai.com</a>.</div><div><br /></div><div>The above code passes on the content of our textarea to ChatGPT, then obtains a reply. Data is sent as a JSON array that looks like this:</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhHCZ0vFb5rDNJEJYvlDfDSumOFYyo-H80c0YBwCllKJoyDr2VBn3QicVc6j4t1P7_eJ8bnZNh57k2lkteM8l5_bUKbLPP3D5QKxJuO4dLe-P3hPh4hYYToMGdtJh4uwVuU4tD1dDo3ZfwmjrMN3pBaXGH_YvdVXkvAXOzYC7MqZ1BEnPJNOQWwgJ_2KQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="100" data-original-width="650" height="98" src="https://blogger.googleusercontent.com/img/a/AVvXsEhHCZ0vFb5rDNJEJYvlDfDSumOFYyo-H80c0YBwCllKJoyDr2VBn3QicVc6j4t1P7_eJ8bnZNh57k2lkteM8l5_bUKbLPP3D5QKxJuO4dLe-P3hPh4hYYToMGdtJh4uwVuU4tD1dDo3ZfwmjrMN3pBaXGH_YvdVXkvAXOzYC7MqZ1BEnPJNOQWwgJ_2KQ=w640-h98" width="640" /></a></div><br /><div>Finally, add the following event handler code for the button that calls the <i>getOpenAIChatCompletions()</i> function:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">const button = document.querySelector("#btn");</span></div></div><div><div><span style="font-family: courier;">button.addEventListener("click", () => {</span></div></div><div><div><span style="font-family: courier;"> getOpenAIChatCompletions();</span></div></div><div><div><span style="font-family: courier;">});</span></div></div></blockquote><div><div><br /></div><div>Our sample application is complete. View the latest version of your page and click on the “Call Completion button. You will receive a response that looks like this:</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgd_AS3VjOuDu45J06QBnRXFmz01hqIXFbRIjl4rfhE5jvE5UvqZLoeqnN7O8KQjOLN2KC48pxLPoLz8bqZ8bTO59VWxkJTfw6lF5rog66dEvJ3Dum2CovC8RWQqeup1YWnjhQB0baJXWiZpNNOuDj8DYan944llwY14kRE3CgC1w8rwtIVxX50BudxbA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="667" data-original-width="827" height="515" src="https://blogger.googleusercontent.com/img/a/AVvXsEgd_AS3VjOuDu45J06QBnRXFmz01hqIXFbRIjl4rfhE5jvE5UvqZLoeqnN7O8KQjOLN2KC48pxLPoLz8bqZ8bTO59VWxkJTfw6lF5rog66dEvJ3Dum2CovC8RWQqeup1YWnjhQB0baJXWiZpNNOuDj8DYan944llwY14kRE3CgC1w8rwtIVxX50BudxbA=w640-h515" width="640" /></a></div><br /><div>The response from ChatGPT is surrounded by *. This tells us that the NBA finals were played in Orlando. Let’s ask about what happened a year earlier in 2019 by posing this additional text:</div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div><span style="font-family: courier;">role: user</span></div></div><div><div><span style="font-family: courier;">content: How about in 2019? Which team won and where was it played?</span></div></div></blockquote><div><div><br /></div><div>Our page now looks like this:</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh8nUJ0Tfei0qsmSoE8UhpUhxfpYuKwLg1-OnRDDAd9rzbzXvZ5en9E4OTiueAWa2rbNLQ5OmilT3ft-1xKde1a-W2CwUcUd6133StsqOufMbhVSwE1oUbw1K5mxU4taulSAWzbzJE52kCa45QcGJgIRXBI5X_vEWUvg9tE1Suj-yT8kv0cqC1hc6Y7EQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="623" data-original-width="773" height="515" src="https://blogger.googleusercontent.com/img/a/AVvXsEh8nUJ0Tfei0qsmSoE8UhpUhxfpYuKwLg1-OnRDDAd9rzbzXvZ5en9E4OTiueAWa2rbNLQ5OmilT3ft-1xKde1a-W2CwUcUd6133StsqOufMbhVSwE1oUbw1K5mxU4taulSAWzbzJE52kCa45QcGJgIRXBI5X_vEWUvg9tE1Suj-yT8kv0cqC1hc6Y7EQ=w640-h515" width="640" /></a></div><br />Click on the “Call Completion” button. </div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh5orwVZQnDaPzBsXn9bRYZ_p86hYWNkZJ_LzLYlkTTFqVy09BqjrpMnfVe3thCzWPqzqtDg51p6tzakug5Ajc2uYfYFQMaqc6_QUfpgZ_2hWQviylwtUJnbTKGfZoWThKqsvkzGeA8n8qBHVb6I5SztN_o8qCin7ZW8fnjqmBIIbcaYKZOY63GiwWduQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="541" data-original-width="676" height="512" src="https://blogger.googleusercontent.com/img/a/AVvXsEh5orwVZQnDaPzBsXn9bRYZ_p86hYWNkZJ_LzLYlkTTFqVy09BqjrpMnfVe3thCzWPqzqtDg51p6tzakug5Ajc2uYfYFQMaqc6_QUfpgZ_2hWQviylwtUJnbTKGfZoWThKqsvkzGeA8n8qBHVb6I5SztN_o8qCin7ZW8fnjqmBIIbcaYKZOY63GiwWduQ=w640-h512" width="640" /></a></div><br /><br /></div><div><div>We are told that the 2019 NBA champions were the Toronto Raptors.</div><div><br /></div><div>You can see from this very simple tutorial that it is quite easy to incorporate ChaGPT in your JavaScript applications.</div><div><br /></div><div>The complete code is given below:</div><div><br /></div><div><span style="font-family: courier;"><!DOCTYPE html></span></div><div><span style="font-family: courier;"><html lang="en"></span></div><div><span style="font-family: courier;"> <head></span></div><div><span style="font-family: courier;"> <title>ChatGPT with JavaScript (NBA)</title></span></div><div><span style="font-family: courier;"> </head></span></div><div><span style="font-family: courier;"> <body></span></div><div><span style="font-family: courier;"> <div></span></div><div><span style="font-family: courier;"> <h1>ChatGPT with JavaScript (NBA)</h1></span></div><div><span style="font-family: courier;"> <textarea id="dialog" cols="100" rows="30"></textarea></span></div><div><span style="font-family: courier;"> <br /></span></div><div><span style="font-family: courier;"> <button id="btn">Call Completion</button></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"> <script></span></div><div><span style="font-family: courier;"> async function getOpenAIChatCompletions() {</span></div><div><span style="font-family: courier;"> const apiKey = "this-is-a-fake-chatgpt-api-key";</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> let text = document.getElementById("dialog").value;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> // remove any * in text</span></div><div><span style="font-family: courier;"> text = text.replace(/\*/g, '');</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> const lines = text.split('\n');</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> // delete empty items in lines array</span></div><div><span style="font-family: courier;"> for (let i = 0; i < lines.length; i++) {</span></div><div><span style="font-family: courier;"> if (lines[i] === '') {</span></div><div><span style="font-family: courier;"> lines.splice(i, 1);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> const result = [];</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> for (let i = 0; i < lines.length; i += 2) {</span></div><div><span style="font-family: courier;"> if (lines[i] === '') {</span></div><div><span style="font-family: courier;"> continue;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> const role = lines[i].split(': ')[1];</span></div><div><span style="font-family: courier;"> const content = lines[i+1].split(': ')[1];</span></div><div><span style="font-family: courier;"> result.push({ role, content });</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> console.log(result);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> fetch("https://api.openai.com/v1/chat/completions", {</span></div><div><span style="font-family: courier;"> method: "POST",</span></div><div><span style="font-family: courier;"> headers: {</span></div><div><span style="font-family: courier;"> "Content-Type": "application/json",</span></div><div><span style="font-family: courier;"> Authorization: `Bearer ${apiKey}`,</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> body: JSON.stringify({</span></div><div><span style="font-family: courier;"> max_tokens: 50,</span></div><div><span style="font-family: courier;"> n: 1,</span></div><div><span style="font-family: courier;"> stop: "\n",</span></div><div><span style="font-family: courier;"> model: "gpt-3.5-turbo",</span></div><div><span style="font-family: courier;"> temperature: 0.5,</span></div><div><span style="font-family: courier;"> messages: result,</span></div><div><span style="font-family: courier;"> }),</span></div><div><span style="font-family: courier;"> })</span></div><div><span style="font-family: courier;"> .then((response) => response.json())</span></div><div><span style="font-family: courier;"> .then((data) => {</span></div><div><span style="font-family: courier;"> const message = data.choices[0].message;</span></div><div><span style="font-family: courier;"> const content = message.content;</span></div><div><span style="font-family: courier;"> const role = message.role;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> const dialog = `role: ${role}\ncontent: ${content}`;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> document.getElementById("dialog").value += "*" + dialog + "*";</span></div><div><span style="font-family: courier;"> });</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> const messages = [</span></div><div><span style="font-family: courier;"> { role: "system", content: "You are a helpful assistant." },</span></div><div><span style="font-family: courier;"> { role: "user", content: "Who won the NBA in 2020?" },</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> role: "assistant",</span></div><div><span style="font-family: courier;"> content: "The Los Angeles Lakers won the NBA in 2020.",</span></div><div><span style="font-family: courier;"> },</span></div><div><span style="font-family: courier;"> { role: "user", content: "Where was it played?" },</span></div><div><span style="font-family: courier;"> ];</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> let defaultMessage = "";</span></div><div><span style="font-family: courier;"> messages.forEach((item) => {</span></div><div><span style="font-family: courier;"> defaultMessage += `role: ${item.role}\ncontent: ${item.content}\n\n`;</span></div><div><span style="font-family: courier;"> });</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> //console.log(defaultMessage);</span></div><div><span style="font-family: courier;"> document.getElementById("dialog").value = defaultMessage;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> const button = document.querySelector("#btn");</span></div><div><span style="font-family: courier;"> button.addEventListener("click", () => {</span></div><div><span style="font-family: courier;"> getOpenAIChatCompletions();</span></div><div><span style="font-family: courier;"> });</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> </script></span></div><div><span style="font-family: courier;"> </body></span></div><div><span style="font-family: courier;"></html></span></div><div><span style="font-family: courier;"><br /></span></div><div>If you click on the <i>Examples </i>tab on the ChatGPT page you can find many examples that you can look into to explore other things that you can do.</div></div><div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjQB6URGytnrErCJJ73BftS8i6MNhH-4JdE0DDq9FO3mIr4IrFl3IY6pIVLtNJGlriVydOfpLsNCcDz-18C9a-4N7lI4l9b6DHm-rcj80AEmhItXfZ5bdHKHkWDkiInMiB2MWk4esSi6CMF9Gi2_mzL-whZz_s60ATIZLANMtFhr53619qcQFlxnqPRmw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="382" data-original-width="692" height="354" src="https://blogger.googleusercontent.com/img/a/AVvXsEjQB6URGytnrErCJJ73BftS8i6MNhH-4JdE0DDq9FO3mIr4IrFl3IY6pIVLtNJGlriVydOfpLsNCcDz-18C9a-4N7lI4l9b6DHm-rcj80AEmhItXfZ5bdHKHkWDkiInMiB2MWk4esSi6CMF9Gi2_mzL-whZz_s60ATIZLANMtFhr53619qcQFlxnqPRmw=w640-h354" width="640" /></a></div><br />The sky is the limit with regards to what is possible.</div><div><br /></div><div><br /></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-16989454157960190932023-06-09T12:08:00.001-07:002023-12-13T21:14:50.050-08:00Azure OpenAI Completion with ASP.NET 7.0 Razor Pages<p>In this tutorial you will learn how to consume the Azure OpenAI Completion service from an ASP.NET 7.0 Razor Pages web application.</p><p>Source code: <a href="https://github.com/medhatelmasry/AOAIRazor">https://github.com/medhatelmasry/AOAIRazor</a></p><h2> Prerequisites</h2><p></p><ol><li>To use Azure OpenAI, you need to have an Azure subscription. This can be obtained by visiting <a href="https://azure.microsoft.com/free/cognitive-services">https://azure.microsoft.com/free/cognitive-services</a>. </li><li>Currently, access to the Azure OpenAI service is granted by application. You can apply at <a href="https://aka.ms/oai/access">https://aka.ms/oai/access</a>.</li></ol><p></p><h2>Getting started with Azure OpenAI service</h2><p>To follow this tutorial, you will need to create an <i>Azure OpenAI</i> service under your Azure subscription. Follow these steps:</p><p>Navigate to the Azure portal at <a href="https://portal.azure.com/">https://portal.azure.com/</a>. </p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhf_8p7VUxwxOcViebE5seWjCGlF0uqFgn-f6yQqL6dW7ElY_vmLSJoEaBgD8jW10TBlnUHwzSOvqJE43lyu8VmqC5FKWVkOrkYiSSvzIR5TewEDOAMnrox_tMhmClpIwwYl8pBb3D6lHx59PMXn3bWkQRJUV5Ie5s8fZQSyAoeDFeKtRwE6ewL9sKjjw" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="511" data-original-width="762" height="215" src="https://blogger.googleusercontent.com/img/a/AVvXsEhf_8p7VUxwxOcViebE5seWjCGlF0uqFgn-f6yQqL6dW7ElY_vmLSJoEaBgD8jW10TBlnUHwzSOvqJE43lyu8VmqC5FKWVkOrkYiSSvzIR5TewEDOAMnrox_tMhmClpIwwYl8pBb3D6lHx59PMXn3bWkQRJUV5Ie5s8fZQSyAoeDFeKtRwE6ewL9sKjjw" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both;">Click on “Create a resource”.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgF-xXEEzrzV-reKOrhccGnJ2gvO0xrSpSOkUcn6h5KEj-P38lCtEgGIG7e6r2f89p9d1i7comb8R0cALN_hAtCrW71zUUjSAYX7cvRaC_yTAl5L5OpzzvNEE-aMhiirULWHelhIyKe3uUYoqEqRWY4koVD7Ezeykc65U_517LQIZ-BldrOwo-f4mBKyQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="219" data-original-width="813" height="86" src="https://blogger.googleusercontent.com/img/a/AVvXsEgF-xXEEzrzV-reKOrhccGnJ2gvO0xrSpSOkUcn6h5KEj-P38lCtEgGIG7e6r2f89p9d1i7comb8R0cALN_hAtCrW71zUUjSAYX7cvRaC_yTAl5L5OpzzvNEE-aMhiirULWHelhIyKe3uUYoqEqRWY4koVD7Ezeykc65U_517LQIZ-BldrOwo-f4mBKyQ" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both;">Enter “openai” in the filter then select “openai”.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhrpzs8OWSVFvsMoPiyDbq1uRLSwlKUUCgiqAgL62Ej74OzCB5LLqszSQPzwpnV4bylTNWQLakaEj5UJVj4gXfZkb0mKs_AxkxPS4bf6PNMxwu4r2l5sz7vFVObcCkcCRwnn2U9wmjhwoCYBVaUeF41dU6yhZSQ4aCTml4ft3ObfR-u-v91KRRrarooFQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="528" data-original-width="644" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhrpzs8OWSVFvsMoPiyDbq1uRLSwlKUUCgiqAgL62Ej74OzCB5LLqszSQPzwpnV4bylTNWQLakaEj5UJVj4gXfZkb0mKs_AxkxPS4bf6PNMxwu4r2l5sz7vFVObcCkcCRwnn2U9wmjhwoCYBVaUeF41dU6yhZSQ4aCTml4ft3ObfR-u-v91KRRrarooFQ" width="293" /></a></div><br />Click on the “Azure OpenAI” card.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi7Xt_uheLgRIck_0IR-EQ93tk9IUrBNZvqi-xn7i0_DfHR9uqJcgO_1FMOT_jAHob7DEmVlF2wyakLRw24TeFZQL6x3_adsuF1Pr3AGyKuDjPZQoDvRh9bocoY-B_0mXG9Ni39Xy-FLx6gVCIgz9fgsrIFc_kWPqPor6L3QB9sSF4tlTaQSX-vHAJvRg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="389" data-original-width="677" height="184" src="https://blogger.googleusercontent.com/img/a/AVvXsEi7Xt_uheLgRIck_0IR-EQ93tk9IUrBNZvqi-xn7i0_DfHR9uqJcgO_1FMOT_jAHob7DEmVlF2wyakLRw24TeFZQL6x3_adsuF1Pr3AGyKuDjPZQoDvRh9bocoY-B_0mXG9Ni39Xy-FLx6gVCIgz9fgsrIFc_kWPqPor6L3QB9sSF4tlTaQSX-vHAJvRg" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Click on the <i>Create </i>button.</div><div class="separator" style="clear: both;"><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgqp8ZKTiG0l0NywdLlabDP9AXgXAi5CVLf6KqHLAHYl9tG3e2YnKPszPCQXE-zfvPsCctvF1WsNv8onCD8UKEC_Lf5WNwvkMat8SEfp1WMa1mDJykV7vQTehbH-tOHCh9RyB9aFTGz4AphLExi6h3Ja5LmX2_mX57HOGfGFG448mfcWGerPMHl_oIaFA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="399" data-original-width="584" height="219" src="https://blogger.googleusercontent.com/img/a/AVvXsEgqp8ZKTiG0l0NywdLlabDP9AXgXAi5CVLf6KqHLAHYl9tG3e2YnKPszPCQXE-zfvPsCctvF1WsNv8onCD8UKEC_Lf5WNwvkMat8SEfp1WMa1mDJykV7vQTehbH-tOHCh9RyB9aFTGz4AphLExi6h3Ja5LmX2_mX57HOGfGFG448mfcWGerPMHl_oIaFA" width="320" /></a></div><br />Choose your subscription then create a new resource group. In my case (as shown above), I created a new resource group named “OpenAI-RG”.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhjZt5VdQUAcOwtpsEgoEJAXDQEGDVquPB75-MRYTRRWB3OzwRguuo-Q7iRNXMXaVkG5QralNzoM4_xdKMkUi0epS1rLfx4Co7HDg0KwcPH66UgbFwyW-hZeCLqgrI4J4FaeVBJ2JCFd_Iim9iS7l1Pvk171fHNbXYPTh-jTtZwklinQNCX5qQCOMm0FQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="691" data-original-width="817" height="541" src="https://blogger.googleusercontent.com/img/a/AVvXsEhjZt5VdQUAcOwtpsEgoEJAXDQEGDVquPB75-MRYTRRWB3OzwRguuo-Q7iRNXMXaVkG5QralNzoM4_xdKMkUi0epS1rLfx4Co7HDg0KwcPH66UgbFwyW-hZeCLqgrI4J4FaeVBJ2JCFd_Iim9iS7l1Pvk171fHNbXYPTh-jTtZwklinQNCX5qQCOMm0FQ=w640-h541" width="640" /></a></div><br />Continue with the selection of a <i>region</i>, provide a <i>instance name </i>(<i>mze-openai</i> in the example above) and select the “Standard S0” pricing tier. Click on the <i>Next </i>button.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjPMo_54QFTCPW0KBSQIsM5niX6TicbHBTkvgUzv5QdVKrxq5E06A-dS38ZSt1dbxSQ0VSY_IcoVpaSNMcnXsbra3IH4W6lq8_V3rWcBykgxfR1eV7Y_GBxJn0rVIzM2XMgabzwdo2xgJixcuyb2Y6DD0WT-5FH6wg5lDzqAO1vF0XcPDmJ6Dz1mYeSug" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="343" data-original-width="758" height="181" src="https://blogger.googleusercontent.com/img/a/AVvXsEjPMo_54QFTCPW0KBSQIsM5niX6TicbHBTkvgUzv5QdVKrxq5E06A-dS38ZSt1dbxSQ0VSY_IcoVpaSNMcnXsbra3IH4W6lq8_V3rWcBykgxfR1eV7Y_GBxJn0rVIzM2XMgabzwdo2xgJixcuyb2Y6DD0WT-5FH6wg5lDzqAO1vF0XcPDmJ6Dz1mYeSug=w400-h181" width="400" /></a></div><br />Accept the default (All networks, including the internet, can access this resource.) on the <i>Network </i>tab then click on the <i>Next </i>button.</div><div class="separator" style="clear: both;"><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhRw08DcfUo6q1jrjl_jO5NsU3UPOK-kZc1BOvFBq5IXw7M_dwxijEwoPFiWs8z2P8dWU2t6t4I3NAjGlJeFx2MXutU1bHI8pmZW7kD0US83JQAAePwoOzpZqMsSAybkswLptLUwwMU59cLsxinPdqADCgRKLY8NaGzcsX9cVev523mBIqmW6k4NWsiQg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="369" data-original-width="1050" height="140" src="https://blogger.googleusercontent.com/img/a/AVvXsEhRw08DcfUo6q1jrjl_jO5NsU3UPOK-kZc1BOvFBq5IXw7M_dwxijEwoPFiWs8z2P8dWU2t6t4I3NAjGlJeFx2MXutU1bHI8pmZW7kD0US83JQAAePwoOzpZqMsSAybkswLptLUwwMU59cLsxinPdqADCgRKLY8NaGzcsX9cVev523mBIqmW6k4NWsiQg=w400-h140" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>On the <i>Tags </i>tab, click on <i>Next </i>without making any changes.</div><div class="separator" style="clear: both;"><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhXOK_aaXbs4oj4R8kLlqysHKFYY-eIuw6xKUjGfJe83SKpg6dKVfGECfkbQy1q9EYihxKcL2LY9TsUMYj7UNXMTFcUKRUWnlAQzPc4Zj0t1BUU9v3HXDMdIbE_oQqDzd3sYzWs7HmI00izAc3gq7sTHw3oPp5UCnwKsvNPhIT3hdkDeBXn6hbxgeBGIw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="730" data-original-width="807" height="362" src="https://blogger.googleusercontent.com/img/a/AVvXsEhXOK_aaXbs4oj4R8kLlqysHKFYY-eIuw6xKUjGfJe83SKpg6dKVfGECfkbQy1q9EYihxKcL2LY9TsUMYj7UNXMTFcUKRUWnlAQzPc4Zj0t1BUU9v3HXDMdIbE_oQqDzd3sYzWs7HmI00izAc3gq7sTHw3oPp5UCnwKsvNPhIT3hdkDeBXn6hbxgeBGIw=w400-h362" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Click the <i>Create </i>button on the “Review + submit” tab. Deployment takes about one minute. </div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEihNiT08d8kBXey3DUzM1kQNrem3DnnYVFJgx1ETf92hmW3AJppcHkY9oA-QXd7EZo1COfROWSs6EDpSbiMOmcP2pM45av-zimu2g7KJTgWGFx37mo9JFAsepVD1vsj9wq5RyxJZpNDn2fDWD-_Of9rt7wkvAansJ783Up7prXBsbK3Ncx9XMhELAYgDw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="491" data-original-width="1048" height="300" src="https://blogger.googleusercontent.com/img/a/AVvXsEihNiT08d8kBXey3DUzM1kQNrem3DnnYVFJgx1ETf92hmW3AJppcHkY9oA-QXd7EZo1COfROWSs6EDpSbiMOmcP2pM45av-zimu2g7KJTgWGFx37mo9JFAsepVD1vsj9wq5RyxJZpNDn2fDWD-_Of9rt7wkvAansJ783Up7prXBsbK3Ncx9XMhELAYgDw=w640-h300" width="640" /></a></div><div class="separator" style="clear: both;"><br /></div>On the <i>Overview </i>blade, click on “Keys and Endpoint” in the left side navigation.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjlG0JINhwnlLVoddukntyMeZQkG-24VB_mY0IF-zMHPgEyzfZcIbHRzLFcmXAmVzGacCuhRqD7eOtlDyZAFTWj9h-S0VrdcHgD0SYlClBoFTrsZJS0ahmZ6c9iXWHlnGJwY1YGSq4rOi3F1fpdsNqc5PSHn9fy1lpnEbt4lG6xkKRLBRQ9FrZiyl47pw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="460" data-original-width="798" height="230" src="https://blogger.googleusercontent.com/img/a/AVvXsEjlG0JINhwnlLVoddukntyMeZQkG-24VB_mY0IF-zMHPgEyzfZcIbHRzLFcmXAmVzGacCuhRqD7eOtlDyZAFTWj9h-S0VrdcHgD0SYlClBoFTrsZJS0ahmZ6c9iXWHlnGJwY1YGSq4rOi3F1fpdsNqc5PSHn9fy1lpnEbt4lG6xkKRLBRQ9FrZiyl47pw=w400-h230" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both;">Copy <i>KEY 1</i> and <i>Endpoint </i>then save the values in a text editor like Notepad.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">We will need to create a model deployment that we can use for text completion. To do this, return to the <i>Overview </i>tab.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg2I8FM5OKYa-CkB8v-VCI8uuNJxXYup5I_YZFWFEZ5qDlFzlFwneC7dCapF0XjcI92BGYlP5N2V58011J8-X7qch3iOY8wT71_ucJZ1shNeuy0eUaNEs_96RMn3nUGxm--9OsYIGybTk4htZUfP3iuWuKUSGZj0m055XjGSoj_T9WtslUu8H1qc4mI9Q" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="328" data-original-width="902" height="145" src="https://blogger.googleusercontent.com/img/a/AVvXsEg2I8FM5OKYa-CkB8v-VCI8uuNJxXYup5I_YZFWFEZ5qDlFzlFwneC7dCapF0XjcI92BGYlP5N2V58011J8-X7qch3iOY8wT71_ucJZ1shNeuy0eUaNEs_96RMn3nUGxm--9OsYIGybTk4htZUfP3iuWuKUSGZj0m055XjGSoj_T9WtslUu8H1qc4mI9Q=w400-h145" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Open “Go to Azure OpenAI Studio” in a new browser tab.</div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjUolcyUxVNseEbo7trIqES4CZIPZ50ed4OvLk2D2Z4IgDsK2ZYdhIjflG30cHHeA4b4If-MakQUQJVVkwNV_rP6qhVeLBumEUkloBEEAc41lWxHR0xDgNwz3nUE2Wh5LyiP_fX8NdmoogaP8edpTxHcflG2Vt4VvObgzx238qZGeIuHrVwdYX13YNBCg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="166" data-original-width="1047" height="102" src="https://blogger.googleusercontent.com/img/a/AVvXsEjUolcyUxVNseEbo7trIqES4CZIPZ50ed4OvLk2D2Z4IgDsK2ZYdhIjflG30cHHeA4b4If-MakQUQJVVkwNV_rP6qhVeLBumEUkloBEEAc41lWxHR0xDgNwz3nUE2Wh5LyiP_fX8NdmoogaP8edpTxHcflG2Vt4VvObgzx238qZGeIuHrVwdYX13YNBCg=w640-h102" width="640" /></a></div><br />Click on “Create new deployment”.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjTQhulgdaLFi6L4VFCRr6Usg9Dh6KjfHX6RYoVhNZjGUNPa4zpstSwWk0KD2QLlmYfcy_I64U_mYLrXvupxfAefdzuVtUpLeVF6VWl17spLCLrRu_1gtnDLzjLhF5jaAgQ7eUCt-ByxYQ6GSca3ZZQoQ7yxJe_G5RIaTNhGl0VcVQWR_VxHPPATmKPuA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="227" data-original-width="594" height="153" src="https://blogger.googleusercontent.com/img/a/AVvXsEjTQhulgdaLFi6L4VFCRr6Usg9Dh6KjfHX6RYoVhNZjGUNPa4zpstSwWk0KD2QLlmYfcy_I64U_mYLrXvupxfAefdzuVtUpLeVF6VWl17spLCLrRu_1gtnDLzjLhF5jaAgQ7eUCt-ByxYQ6GSca3ZZQoQ7yxJe_G5RIaTNhGl0VcVQWR_VxHPPATmKPuA=w400-h153" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Click on “+ Create new deployment”.<br /><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjNi_DCDuxL50Z1VRHv1gqKjRSdcsQuQzyJMf8jr8jFMO5rzEIhqZ1twBmqXeyF6-nJmlqX9Qwq8AenL3eafWkVHGx8DhMK7d-MjVB5K6AMxYFmgQ-peapVgpXoXMmASK1jbzMJb3kGz0TYkadSQRApaVLinoYgca5pTlTWLFicn1nv9UcdEIzu_ucZxQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="603" data-original-width="888" height="271" src="https://blogger.googleusercontent.com/img/a/AVvXsEjNi_DCDuxL50Z1VRHv1gqKjRSdcsQuQzyJMf8jr8jFMO5rzEIhqZ1twBmqXeyF6-nJmlqX9Qwq8AenL3eafWkVHGx8DhMK7d-MjVB5K6AMxYFmgQ-peapVgpXoXMmASK1jbzMJb3kGz0TYkadSQRApaVLinoYgca5pTlTWLFicn1nv9UcdEIzu_ucZxQ=w400-h271" width="400" /></a></div><br /><div>For the model, select “gpt-35-turbo” and give the deployment a name which you will need to remember as this will be configured in the app that we will soon develop. I called the deployment name <i>gpt35-turbo-deployment</i>. Click on the <i>Create </i>button.</div><div><br /></div><div>As a summary, we will need the following parameters in our Razor Pages application:</div></div><div><br /></div><table><tbody><tr><th>Setting</th><th>Value</th></tr><tr><td>KEY 1</td><td>this-is-a-fake-api-key</td></tr><tr><td>Endpoint</td><td>https://mze-openai.openai.azure.com/</td></tr><tr><td>Model deployment</td><td>gpt35-turbo-deployment</td></tr><tr><td>ApiVersion</td><td>2022-12-01</td></tr></tbody></table><div><br /></div><div><div>Next, we will create our ASP.NET Razor Pages web application.</div><h2>ASP.NET Razor Pages application</h2><div>In a working directory on your computer, open a terminal window. Enter the following command to create an ASP.NET Razor Pages web application:</div><div><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both;"><div><div><div><span style="font-family: courier;">dotnet new razor -f net7.0 -n AOAIRazor</span></div></div></div></div></div><div><div class="separator" style="clear: both;"><div><div><div><span style="font-family: courier;">cd AOAIRazor</span></div></div></div></div></div><div><div class="separator" style="clear: both;"><div><div><div><span style="font-family: courier;">dotnet add package AzureOpenAIClient -v 1.0</span></div></div></div></div></div></blockquote><div><div class="separator" style="clear: both;"><div><div><br /></div><div>Open your application in VS Code by entering the following command in a terminal window:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">code .</span></div><h2>Configuration settings</h2><div>Open the <i>appsettings.json</i> configurations file in VS Code and add the parameters obtained from Azure. In my case, these are the parameters I added:</div><div><br /></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div><div class="separator" style="clear: both;"><div><div><span style="font-family: courier;">"OpenAiClientConfiguration": {</span></div></div></div></div><div><div class="separator" style="clear: both;"><div><div><span style="font-family: courier;"> "BaseUri": "https://mze-openai.openai.azure.com/",</span></div></div></div></div><div><div class="separator" style="clear: both;"><div><div><span style="font-family: courier;"> "ApiKey": "this-is-a-fake-api-key",</span></div></div></div></div><div><div class="separator" style="clear: both;"><div><div><span style="font-family: courier;"> "DeploymentName": "gpt35-turbo-deployment",</span></div></div></div></div><div><div class="separator" style="clear: both;"><div><div><span style="font-family: courier;"> "ApiVersion": "2022-12-01"</span></div></div></div></div><div><div class="separator" style="clear: both;"><div><div><span style="font-family: courier;">},</span></div></div></div></div></blockquote><div><div class="separator" style="clear: both;"><div><h2>Service Class</h2><div>In a <i>Services </i>folder, create a C# file named <i>AzureOpenAIService.cs</i> with the following content:</div><div><br /></div><div><div><span style="font-family: courier;">public class AzureOpenAIService {</span></div><div><span style="font-family: courier;"> private readonly OpenAIClient _openAiClient;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public AzureOpenAIService(OpenAIClient client) {</span></div><div><span style="font-family: courier;"> _openAiClient = client;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public async Task<CompletionResponse?> GetTextCompletionResponse(string input, int maxTokens) {</span></div><div><span style="font-family: courier;"> var completionRequest = new CompletionRequest() {</span></div><div><span style="font-family: courier;"> Prompt = input,</span></div><div><span style="font-family: courier;"> MaxTokens = maxTokens</span></div><div><span style="font-family: courier;"> };</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> return await _openAiClient.GetTextCompletionResponseAsync(completionRequest);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div></div><div><br /></div><div>The code defines a class <i>AzureOpenAIService </i>which acts as a service that wraps the functionality of the <i>OpenAIClient </i>class.</div><div><br /></div><div>The <i>AzureOpenAIService </i>class takes in an instance of <i>OpenAIClient </i>using dependency injection and stores it in a private variable <i>_openAiClient</i>.</div><div><br /></div><div>The class has a single public method <i>GetTextCompletionResponse </i>that takes in two arguments, <i>input </i>and <i>maxTokens</i>, and returns a <i>CompletionResponse </i>as an asynchronous task.</div><div><br /></div><div>The <i>GetTextCompletionResponse </i>method calls the <i>GetTextCompletionResponseAsync </i>method on the <i>_openAiClient</i>, passing in the <i>completionRequest </i>object, and returns the result as a <i>CompletionResponse</i>.</div><div><br /></div><div>Note that the <i>CompletionRequest </i>class allows for other parameters to be passed such as <i>Temperature</i>, <i>FrequencyPenalty </i>and <i>PresencePenalty</i>.</div><div><br /></div><div>Add this line of code to <i>Program.cs</i> right above <i>var app = builder.Build();</i>:</div><div><br /></div><div><span style="color: #38761d; font-family: courier;">// OpenAIClient</span></div><div><div><span style="font-family: courier;">builder.Services.AddOpenAIClient(x => builder.Configuration.Bind(nameof(OpenAIClientConfiguration), x));</span></div><div><span style="font-family: courier;">builder.Services.AddScoped<AzureOpenAIService>();</span></div></div><h2>Razor Page</h2><div>In the <i>Pages </i>folder, replace <i>Index.cshtml</i> with the following content:</div><div><br /></div><div><div><span style="font-family: courier;">@page</span></div><div><span style="font-family: courier;">@model IndexModel</span></div><div><span style="font-family: courier;">@{</span></div><div><span style="font-family: courier;"> ViewData["Title"] = "Azure OpenAI Completion Example";</span></div><div><span style="font-family: courier;">}</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><h2>@ViewData["Title"]</h2></span></div><div><span style="font-family: courier;"><div class="row"></span></div><div><span style="font-family: courier;"> <div class="col-md-8"></span></div><div><span style="font-family: courier;"> <form method="post"></span></div><div><span style="font-family: courier;"> <div asp-validation-summary="ModelOnly" class="text-danger"></div></span></div><div><span style="font-family: courier;"> </span></div><div><span style="font-family: courier;"> <div class="form-group"></span></div><div><span style="font-family: courier;"> <textarea asp-for="TextValue" class="form-control" rows="15"></textarea></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> <div class="form-group"></span></div><div><span style="font-family: courier;"> <input type="submit" value="Call Completion" class="btn btn-primary" /></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"> </form></span></div><div><span style="font-family: courier;"> </div></span></div><div><span style="font-family: courier;"></div></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">@section Scripts {</span></div><div><span style="font-family: courier;"> @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}</span></div><div><span style="font-family: courier;">}</span></div></div><div><br /></div><div><div>The above code displays a textarea and a button. Data in the textarea is bound to a variable named <i>TextValue </i>that is bound to a property in the code-behind file named <i>Index.cshtml.cs</i>.</div><div><br /></div><div>Next, replace the content of <i>Index.cshtml.cs</i> with the following code:</div></div><div><br /></div><div><div><span style="font-family: courier;">using Microsoft.AspNetCore.Mvc;</span></div><div><span style="font-family: courier;">using Microsoft.AspNetCore.Mvc.RazorPages;</span></div><div><span style="font-family: courier;">using AOAIRazor.Services;</span></div><div><span style="font-family: courier;">using AzureOpenAIClient.Http;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">namespace AOAIRazor.Pages;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">public class IndexModel : PageModel {</span></div><div><span style="font-family: courier;"> CompletionResponse? completionResponse;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> [BindProperty]</span></div><div><span style="font-family: courier;"> public string TextValue { get; set; } = default!;</span></div><div><span style="font-family: courier;"> </span></div><div><span style="font-family: courier;"> private readonly ILogger<IndexModel> _logger;</span></div><div><span style="font-family: courier;"> private readonly AzureOpenAIService _azureOpenAIService ;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public IndexModel(ILogger<IndexModel> logger, </span></div><div><span style="font-family: courier;"> AzureOpenAIService azureOpenAIService) {</span></div><div><span style="font-family: courier;"> _logger = logger;</span></div><div><span style="font-family: courier;"> _azureOpenAIService = azureOpenAIService;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public IActionResult OnGetAsync() {</span></div><div><span style="font-family: courier;"> // Retrieve the value of TextValue from TempData</span></div><div><span style="font-family: courier;"> if (TempData.ContainsKey("TextValue")) {</span></div><div><span style="font-family: courier;"> TextValue = (string)TempData["TextValue"]!;</span></div><div><span style="font-family: courier;"> } else {</span></div><div><span style="font-family: courier;"> TextValue = "Four score and seven years ago";</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> return Page();</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public async Task<IActionResult> OnPostAsync() {</span></div><div><span style="font-family: courier;"> </span></div><div><span style="font-family: courier;"> if (TextValue != null) {</span></div><div><span style="font-family: courier;"> completionResponse = await _azureOpenAIService.GetTextCompletionResponse(TextValue, 500);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (completionResponse?.Choices.Count > 0) {</span></div><div><span style="font-family: courier;"> TextValue = TextValue + completionResponse.Choices[0].Text;</span></div><div><span style="font-family: courier;"> </span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> // Store the value of TextValue in TempData</span></div><div><span style="font-family: courier;"> TempData["TextValue"] = TextValue;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> // Do other processing as needed</span></div><div><span style="font-family: courier;"> return RedirectToPage();</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div></div><div><br /></div><div><div>The <i>OnGetAsync </i>sets a default value of "Four score and seven years ago" to <i>TextValue</i>.</div><div><br /></div><div>The <i>OnPostAsync </i>method checks if the <i>TextValue </i>is not null. If it's not, it calls the <i>GetTextCompletionResponse </i>method on the <i>_azureOpenAIService </i>instance, passing in <i>TextValue </i>and 500 as the <i>maxTokens </i>argument. The response from the method is stored in the <i>completionResponse </i>variable.</div><div><br /></div><div>After that, it checks if the <i>Choices </i>property of <i>Completion </i>has a count greater than 0. If it does, it appends the <i>Choices[0]</i> text to <i>TextValue.</i></div></div><div><br /></div><div>Run the application by executing the following terminal command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></div><div><br /></div><div>The application will start in a browser. </div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi4gomb8vMji22chYMGKzlPuFaPFqBicx9-8NcJAuE6iQMN3r7OyMom33L52eieQMOjC0RDQPVV7Bh-3aWS0CuP-GWhRjm0My72jsBzMYKBn28y0OQfofo4a0_l5t6QHcUmnQR8rLCBEZqY06FdUpd4A7xXni1krnZnIBhG4r8rWCZJKxaOkxrx05zuNQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="549" data-original-width="781" height="281" src="https://blogger.googleusercontent.com/img/a/AVvXsEi4gomb8vMji22chYMGKzlPuFaPFqBicx9-8NcJAuE6iQMN3r7OyMom33L52eieQMOjC0RDQPVV7Bh-3aWS0CuP-GWhRjm0My72jsBzMYKBn28y0OQfofo4a0_l5t6QHcUmnQR8rLCBEZqY06FdUpd4A7xXni1krnZnIBhG4r8rWCZJKxaOkxrx05zuNQ=w400-h281" width="400" /></a></div></div><br />In the above scenario, you start with a default statement “Four score and seven years ago”. Click on “Call Completion” and after about 20 seconds you will experience CharGPT completing the statement.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgXOGOMMEKBqvcHaw8foRBJ2BFJ3Pe2vP1Q5RFPrDWYid62BGyQdZP98bMHEXEegNfKN54cRXHIiVJnISLNdVJOaiycIkhTo-XLmSACLPjzY2OuYKVJVQYYz9DBqoU9WYgguXnPhxHJFWOsTYQNX7v0v7fuvvAH-r8V65SR5o1wmYde7_7AnF_QrVb5vQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="581" data-original-width="803" height="290" src="https://blogger.googleusercontent.com/img/a/AVvXsEgXOGOMMEKBqvcHaw8foRBJ2BFJ3Pe2vP1Q5RFPrDWYid62BGyQdZP98bMHEXEegNfKN54cRXHIiVJnISLNdVJOaiycIkhTo-XLmSACLPjzY2OuYKVJVQYYz9DBqoU9WYgguXnPhxHJFWOsTYQNX7v0v7fuvvAH-r8V65SR5o1wmYde7_7AnF_QrVb5vQ=w400-h290" width="400" /></a></div></div><br />This tutorial should help you explore all kinds of ways to incorporate the power of ChatGPT into your ASP.NET web applications through the Azure OpenAPI service.</div><br /></div></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0tag:blogger.com,1999:blog-1773821877020177026.post-31288889353066440352023-06-08T10:35:00.006-07:002023-06-09T11:54:41.470-07:00Azure OpenAI Completion & Server-side Blazor<p>In this tutorial you will learn how to use the Azure OpenAI Completion service from a server-side Blazor application using .NET 7.0.</p><p>Source code: <a href="https://github.com/medhatelmasry/AOAIServerSideBlazor">https://github.com/medhatelmasry/AOAIServerSideBlazor</a></p><h2 style="text-align: left;"> Prerequisites</h2><p></p><ol style="text-align: left;"><li>To use Azure OpenAI, you need to have an Azure subscription. This can be obtained by visiting <a href="https://azure.microsoft.com/free/cognitive-services">https://azure.microsoft.com/free/cognitive-services</a>. </li><li>Currently, access to the Azure OpenAI service is granted by application. You can apply at <a href="https://aka.ms/oai/access">https://aka.ms/oai/access</a>.</li></ol><p></p><h2 style="text-align: left;">Getting started with Azure OpenAI service</h2><p>To follow this tutorial, you will need to create an <i>Azure OpenAI</i> service under your Azure subscription. Follow these steps:</p><p>Navigate to the Azure portal at <a href="https://portal.azure.com/">https://portal.azure.com/</a>. </p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhf_8p7VUxwxOcViebE5seWjCGlF0uqFgn-f6yQqL6dW7ElY_vmLSJoEaBgD8jW10TBlnUHwzSOvqJE43lyu8VmqC5FKWVkOrkYiSSvzIR5TewEDOAMnrox_tMhmClpIwwYl8pBb3D6lHx59PMXn3bWkQRJUV5Ie5s8fZQSyAoeDFeKtRwE6ewL9sKjjw" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="511" data-original-width="762" height="215" src="https://blogger.googleusercontent.com/img/a/AVvXsEhf_8p7VUxwxOcViebE5seWjCGlF0uqFgn-f6yQqL6dW7ElY_vmLSJoEaBgD8jW10TBlnUHwzSOvqJE43lyu8VmqC5FKWVkOrkYiSSvzIR5TewEDOAMnrox_tMhmClpIwwYl8pBb3D6lHx59PMXn3bWkQRJUV5Ie5s8fZQSyAoeDFeKtRwE6ewL9sKjjw" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Click on “Create a resource”.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgF-xXEEzrzV-reKOrhccGnJ2gvO0xrSpSOkUcn6h5KEj-P38lCtEgGIG7e6r2f89p9d1i7comb8R0cALN_hAtCrW71zUUjSAYX7cvRaC_yTAl5L5OpzzvNEE-aMhiirULWHelhIyKe3uUYoqEqRWY4koVD7Ezeykc65U_517LQIZ-BldrOwo-f4mBKyQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="219" data-original-width="813" height="86" src="https://blogger.googleusercontent.com/img/a/AVvXsEgF-xXEEzrzV-reKOrhccGnJ2gvO0xrSpSOkUcn6h5KEj-P38lCtEgGIG7e6r2f89p9d1i7comb8R0cALN_hAtCrW71zUUjSAYX7cvRaC_yTAl5L5OpzzvNEE-aMhiirULWHelhIyKe3uUYoqEqRWY4koVD7Ezeykc65U_517LQIZ-BldrOwo-f4mBKyQ" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Enter “openai” in the filter then select “openai”.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhrpzs8OWSVFvsMoPiyDbq1uRLSwlKUUCgiqAgL62Ej74OzCB5LLqszSQPzwpnV4bylTNWQLakaEj5UJVj4gXfZkb0mKs_AxkxPS4bf6PNMxwu4r2l5sz7vFVObcCkcCRwnn2U9wmjhwoCYBVaUeF41dU6yhZSQ4aCTml4ft3ObfR-u-v91KRRrarooFQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="528" data-original-width="644" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhrpzs8OWSVFvsMoPiyDbq1uRLSwlKUUCgiqAgL62Ej74OzCB5LLqszSQPzwpnV4bylTNWQLakaEj5UJVj4gXfZkb0mKs_AxkxPS4bf6PNMxwu4r2l5sz7vFVObcCkcCRwnn2U9wmjhwoCYBVaUeF41dU6yhZSQ4aCTml4ft3ObfR-u-v91KRRrarooFQ" width="293" /></a></div><br />Click on the “Azure OpenAI” card.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi7Xt_uheLgRIck_0IR-EQ93tk9IUrBNZvqi-xn7i0_DfHR9uqJcgO_1FMOT_jAHob7DEmVlF2wyakLRw24TeFZQL6x3_adsuF1Pr3AGyKuDjPZQoDvRh9bocoY-B_0mXG9Ni39Xy-FLx6gVCIgz9fgsrIFc_kWPqPor6L3QB9sSF4tlTaQSX-vHAJvRg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="389" data-original-width="677" height="184" src="https://blogger.googleusercontent.com/img/a/AVvXsEi7Xt_uheLgRIck_0IR-EQ93tk9IUrBNZvqi-xn7i0_DfHR9uqJcgO_1FMOT_jAHob7DEmVlF2wyakLRw24TeFZQL6x3_adsuF1Pr3AGyKuDjPZQoDvRh9bocoY-B_0mXG9Ni39Xy-FLx6gVCIgz9fgsrIFc_kWPqPor6L3QB9sSF4tlTaQSX-vHAJvRg" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Click on the <i>Create </i>button.</div><div class="separator" style="clear: both; text-align: left;"><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgqp8ZKTiG0l0NywdLlabDP9AXgXAi5CVLf6KqHLAHYl9tG3e2YnKPszPCQXE-zfvPsCctvF1WsNv8onCD8UKEC_Lf5WNwvkMat8SEfp1WMa1mDJykV7vQTehbH-tOHCh9RyB9aFTGz4AphLExi6h3Ja5LmX2_mX57HOGfGFG448mfcWGerPMHl_oIaFA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="399" data-original-width="584" height="219" src="https://blogger.googleusercontent.com/img/a/AVvXsEgqp8ZKTiG0l0NywdLlabDP9AXgXAi5CVLf6KqHLAHYl9tG3e2YnKPszPCQXE-zfvPsCctvF1WsNv8onCD8UKEC_Lf5WNwvkMat8SEfp1WMa1mDJykV7vQTehbH-tOHCh9RyB9aFTGz4AphLExi6h3Ja5LmX2_mX57HOGfGFG448mfcWGerPMHl_oIaFA" width="320" /></a></div><br />Choose your subscription then create a new resource group. In my case (as shown above), I created a new resource group named “OpenAI-RG”.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhjZt5VdQUAcOwtpsEgoEJAXDQEGDVquPB75-MRYTRRWB3OzwRguuo-Q7iRNXMXaVkG5QralNzoM4_xdKMkUi0epS1rLfx4Co7HDg0KwcPH66UgbFwyW-hZeCLqgrI4J4FaeVBJ2JCFd_Iim9iS7l1Pvk171fHNbXYPTh-jTtZwklinQNCX5qQCOMm0FQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="691" data-original-width="817" height="541" src="https://blogger.googleusercontent.com/img/a/AVvXsEhjZt5VdQUAcOwtpsEgoEJAXDQEGDVquPB75-MRYTRRWB3OzwRguuo-Q7iRNXMXaVkG5QralNzoM4_xdKMkUi0epS1rLfx4Co7HDg0KwcPH66UgbFwyW-hZeCLqgrI4J4FaeVBJ2JCFd_Iim9iS7l1Pvk171fHNbXYPTh-jTtZwklinQNCX5qQCOMm0FQ=w640-h541" width="640" /></a></div><br />Continue with the selection of a <i>region</i>, provide a <i>instance name </i>(mze-openai in the example above) and select the “Standard S0” pricing tier. Click on the <i>Next </i>button.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjPMo_54QFTCPW0KBSQIsM5niX6TicbHBTkvgUzv5QdVKrxq5E06A-dS38ZSt1dbxSQ0VSY_IcoVpaSNMcnXsbra3IH4W6lq8_V3rWcBykgxfR1eV7Y_GBxJn0rVIzM2XMgabzwdo2xgJixcuyb2Y6DD0WT-5FH6wg5lDzqAO1vF0XcPDmJ6Dz1mYeSug" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="343" data-original-width="758" height="181" src="https://blogger.googleusercontent.com/img/a/AVvXsEjPMo_54QFTCPW0KBSQIsM5niX6TicbHBTkvgUzv5QdVKrxq5E06A-dS38ZSt1dbxSQ0VSY_IcoVpaSNMcnXsbra3IH4W6lq8_V3rWcBykgxfR1eV7Y_GBxJn0rVIzM2XMgabzwdo2xgJixcuyb2Y6DD0WT-5FH6wg5lDzqAO1vF0XcPDmJ6Dz1mYeSug=w400-h181" width="400" /></a></div><br />Accept the default (All networks, including the internet, can access this resource.) on the <i>Network </i>tab then click on the <i>Next </i>button.</div><div class="separator" style="clear: both; text-align: left;"><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhRw08DcfUo6q1jrjl_jO5NsU3UPOK-kZc1BOvFBq5IXw7M_dwxijEwoPFiWs8z2P8dWU2t6t4I3NAjGlJeFx2MXutU1bHI8pmZW7kD0US83JQAAePwoOzpZqMsSAybkswLptLUwwMU59cLsxinPdqADCgRKLY8NaGzcsX9cVev523mBIqmW6k4NWsiQg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="369" data-original-width="1050" height="140" src="https://blogger.googleusercontent.com/img/a/AVvXsEhRw08DcfUo6q1jrjl_jO5NsU3UPOK-kZc1BOvFBq5IXw7M_dwxijEwoPFiWs8z2P8dWU2t6t4I3NAjGlJeFx2MXutU1bHI8pmZW7kD0US83JQAAePwoOzpZqMsSAybkswLptLUwwMU59cLsxinPdqADCgRKLY8NaGzcsX9cVev523mBIqmW6k4NWsiQg=w400-h140" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>On the <i>Tags </i>tab, click on <i>Next </i>without making any changes.</div><div class="separator" style="clear: both; text-align: left;"><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhXOK_aaXbs4oj4R8kLlqysHKFYY-eIuw6xKUjGfJe83SKpg6dKVfGECfkbQy1q9EYihxKcL2LY9TsUMYj7UNXMTFcUKRUWnlAQzPc4Zj0t1BUU9v3HXDMdIbE_oQqDzd3sYzWs7HmI00izAc3gq7sTHw3oPp5UCnwKsvNPhIT3hdkDeBXn6hbxgeBGIw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="730" data-original-width="807" height="362" src="https://blogger.googleusercontent.com/img/a/AVvXsEhXOK_aaXbs4oj4R8kLlqysHKFYY-eIuw6xKUjGfJe83SKpg6dKVfGECfkbQy1q9EYihxKcL2LY9TsUMYj7UNXMTFcUKRUWnlAQzPc4Zj0t1BUU9v3HXDMdIbE_oQqDzd3sYzWs7HmI00izAc3gq7sTHw3oPp5UCnwKsvNPhIT3hdkDeBXn6hbxgeBGIw=w400-h362" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Click the <i>Create </i>button on the “Review + submit” tab. Deployment takes about one minute. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEihNiT08d8kBXey3DUzM1kQNrem3DnnYVFJgx1ETf92hmW3AJppcHkY9oA-QXd7EZo1COfROWSs6EDpSbiMOmcP2pM45av-zimu2g7KJTgWGFx37mo9JFAsepVD1vsj9wq5RyxJZpNDn2fDWD-_Of9rt7wkvAansJ783Up7prXBsbK3Ncx9XMhELAYgDw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="491" data-original-width="1048" height="300" src="https://blogger.googleusercontent.com/img/a/AVvXsEihNiT08d8kBXey3DUzM1kQNrem3DnnYVFJgx1ETf92hmW3AJppcHkY9oA-QXd7EZo1COfROWSs6EDpSbiMOmcP2pM45av-zimu2g7KJTgWGFx37mo9JFAsepVD1vsj9wq5RyxJZpNDn2fDWD-_Of9rt7wkvAansJ783Up7prXBsbK3Ncx9XMhELAYgDw=w640-h300" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div>On the <i>Overview </i>blade, click on “Keys and Endpoint” in the left side navigation.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjlG0JINhwnlLVoddukntyMeZQkG-24VB_mY0IF-zMHPgEyzfZcIbHRzLFcmXAmVzGacCuhRqD7eOtlDyZAFTWj9h-S0VrdcHgD0SYlClBoFTrsZJS0ahmZ6c9iXWHlnGJwY1YGSq4rOi3F1fpdsNqc5PSHn9fy1lpnEbt4lG6xkKRLBRQ9FrZiyl47pw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="460" data-original-width="798" height="230" src="https://blogger.googleusercontent.com/img/a/AVvXsEjlG0JINhwnlLVoddukntyMeZQkG-24VB_mY0IF-zMHPgEyzfZcIbHRzLFcmXAmVzGacCuhRqD7eOtlDyZAFTWj9h-S0VrdcHgD0SYlClBoFTrsZJS0ahmZ6c9iXWHlnGJwY1YGSq4rOi3F1fpdsNqc5PSHn9fy1lpnEbt4lG6xkKRLBRQ9FrZiyl47pw=w400-h230" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both;">Copy <i>KEY 1</i> and <i>Endpoint </i>then save the values in a text editor like Notepad.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">We will need to create a model deployment that we can use for text completion. To do this, return to the <i>Overview </i>tab.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg2I8FM5OKYa-CkB8v-VCI8uuNJxXYup5I_YZFWFEZ5qDlFzlFwneC7dCapF0XjcI92BGYlP5N2V58011J8-X7qch3iOY8wT71_ucJZ1shNeuy0eUaNEs_96RMn3nUGxm--9OsYIGybTk4htZUfP3iuWuKUSGZj0m055XjGSoj_T9WtslUu8H1qc4mI9Q" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="328" data-original-width="902" height="145" src="https://blogger.googleusercontent.com/img/a/AVvXsEg2I8FM5OKYa-CkB8v-VCI8uuNJxXYup5I_YZFWFEZ5qDlFzlFwneC7dCapF0XjcI92BGYlP5N2V58011J8-X7qch3iOY8wT71_ucJZ1shNeuy0eUaNEs_96RMn3nUGxm--9OsYIGybTk4htZUfP3iuWuKUSGZj0m055XjGSoj_T9WtslUu8H1qc4mI9Q=w400-h145" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Open “Go to Azure OpenAI Studio” in a new browser tab.</div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjUolcyUxVNseEbo7trIqES4CZIPZ50ed4OvLk2D2Z4IgDsK2ZYdhIjflG30cHHeA4b4If-MakQUQJVVkwNV_rP6qhVeLBumEUkloBEEAc41lWxHR0xDgNwz3nUE2Wh5LyiP_fX8NdmoogaP8edpTxHcflG2Vt4VvObgzx238qZGeIuHrVwdYX13YNBCg" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="166" data-original-width="1047" height="102" src="https://blogger.googleusercontent.com/img/a/AVvXsEjUolcyUxVNseEbo7trIqES4CZIPZ50ed4OvLk2D2Z4IgDsK2ZYdhIjflG30cHHeA4b4If-MakQUQJVVkwNV_rP6qhVeLBumEUkloBEEAc41lWxHR0xDgNwz3nUE2Wh5LyiP_fX8NdmoogaP8edpTxHcflG2Vt4VvObgzx238qZGeIuHrVwdYX13YNBCg=w640-h102" width="640" /></a></div><br />Click on “Create new deployment”.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjTQhulgdaLFi6L4VFCRr6Usg9Dh6KjfHX6RYoVhNZjGUNPa4zpstSwWk0KD2QLlmYfcy_I64U_mYLrXvupxfAefdzuVtUpLeVF6VWl17spLCLrRu_1gtnDLzjLhF5jaAgQ7eUCt-ByxYQ6GSca3ZZQoQ7yxJe_G5RIaTNhGl0VcVQWR_VxHPPATmKPuA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="227" data-original-width="594" height="153" src="https://blogger.googleusercontent.com/img/a/AVvXsEjTQhulgdaLFi6L4VFCRr6Usg9Dh6KjfHX6RYoVhNZjGUNPa4zpstSwWk0KD2QLlmYfcy_I64U_mYLrXvupxfAefdzuVtUpLeVF6VWl17spLCLrRu_1gtnDLzjLhF5jaAgQ7eUCt-ByxYQ6GSca3ZZQoQ7yxJe_G5RIaTNhGl0VcVQWR_VxHPPATmKPuA=w400-h153" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>Click on “+ Create new deployment”.<br /><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjNi_DCDuxL50Z1VRHv1gqKjRSdcsQuQzyJMf8jr8jFMO5rzEIhqZ1twBmqXeyF6-nJmlqX9Qwq8AenL3eafWkVHGx8DhMK7d-MjVB5K6AMxYFmgQ-peapVgpXoXMmASK1jbzMJb3kGz0TYkadSQRApaVLinoYgca5pTlTWLFicn1nv9UcdEIzu_ucZxQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="603" data-original-width="888" height="271" src="https://blogger.googleusercontent.com/img/a/AVvXsEjNi_DCDuxL50Z1VRHv1gqKjRSdcsQuQzyJMf8jr8jFMO5rzEIhqZ1twBmqXeyF6-nJmlqX9Qwq8AenL3eafWkVHGx8DhMK7d-MjVB5K6AMxYFmgQ-peapVgpXoXMmASK1jbzMJb3kGz0TYkadSQRApaVLinoYgca5pTlTWLFicn1nv9UcdEIzu_ucZxQ=w400-h271" width="400" /></a></div><br /><div>For the model, select “gpt-35-turbo” and give the deployment a name which you will need to remember as this will be configured in the app that we will soon develop. I called the deployment name <i>gpt35-turbo-deployment</i>. Click on the <i>Create </i>button.</div><div><br /></div><div>As a summary, we will need the following parameters in our Blazor application:</div></div>
<table>
<tbody><tr><th>Setting</th><th>Value</th></tr>
<tr>
<td>KEY 1</td>
<td>this-is-a-fake-api-key</td>
</tr>
<tr>
<td>Endpoint</td>
<td>https://mze-openai.openai.azure.com/</td>
</tr>
<tr>
<td>Model deployment</td>
<td>gpt35-turbo-deployment</td>
</tr>
<tr>
<td>ApiVersion</td>
<td>2022-12-01</td>
</tr>
</tbody></table>
<div><br /></div><div><div>Next, we will create our server-side Blazor application.</div><h2 style="text-align: left;">Server-side Blazor application</h2><div>In a working directory on your computer, open a terminal window. Enter the following command to create a Server-side Blazor application:</div><div><br /></div><div><span style="font-family: courier;">dotnet new blazorserver -f net7.0 -n AOAIServerSideBlazor</span></div><div><span style="font-family: courier;">cd AOAIServerSideBlazor</span></div><div><span style="font-family: courier;">dotnet add package AzureOpenAIClient -v 1.0</span></div><div><br /></div><div>Open your application in VS Code by entering the following command in a terminal window:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">code .</span></div><h2 style="text-align: left;">Configuration settings</h2><div>Open the <i>appsettings.json</i> configurations file in VS Code and add the parameters obtained from Azure. In my case, these are the parameters I added:</div><div><br /></div><div><span style="font-family: courier;">"OpenAiClientConfiguration": {</span></div><div><span style="font-family: courier;"> "BaseUri": "https://mze-openai.openai.azure.com/",</span></div><div><span style="font-family: courier;"> "ApiKey": "this-is-a-fake-api-key",</span></div><div><span style="font-family: courier;"> "DeploymentName": "gpt35-turbo-deployment",</span></div><div><span style="font-family: courier;"> "ApiVersion": "2022-12-01"</span></div><div><span style="font-family: courier;">},</span></div><h2 style="text-align: left;">Service Class</h2><div>In the Data folder, create a C# file named <i>AzureOpenAIService.cs</i> with the following content:</div><div><br /></div><div><span style="font-family: courier;">public class AzureOpenAIService {</span></div><div><span style="font-family: courier;"> private readonly OpenAIClient _openAiClient;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public AzureOpenAIService(OpenAIClient client)</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> _openAiClient = client;</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> public async Task<CompletionResponse?> GetTextCompletionResponse(string input, int maxTokens) {</span></div><div><span style="font-family: courier;"> var completionRequest = new CompletionRequest() {</span></div><div><span style="font-family: courier;"> Prompt = input,</span></div><div><span style="font-family: courier;"> MaxTokens = maxTokens</span></div><div><span style="font-family: courier;"> };</span></div><div><span style="font-family: courier;"> </span></div><div><span style="font-family: courier;"> return await _openAiClient.GetTextCompletionResponseAsync(completionRequest);</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div><div><br /></div><div>The code defines a class <i>AzureOpenAIService </i>which acts as a service that wraps the functionality of the <i>OpenAIClient </i>class.</div><div><br /></div><div>The <i>AzureOpenAIService </i>class takes in an instance of <i>OpenAIClient </i>using dependency injection and stores it in a private variable <i>_openAiClient</i>.</div><div><br /></div><div>The class has a single public method <i>GetTextCompletionResponse </i>that takes in two arguments, input and <i>maxTokens</i>, and returns a <i>CompletionResponse </i>as an asynchronous task.</div><div><br /></div><div>The <i>GetTextCompletionResponse </i>method calls the <i>GetTextCompletionResponseAsync </i>method on the <i>_openAiClient</i>, passing in the <i>completionRequest </i>object, and returns the result as a <i>CompletionResponse</i>.</div><div><br /></div><div>Note that the <i>CompletionRequest </i>class allows for other parameters to be passed such as <i>Temperature</i>, <i>FrequencyPenalty </i>and <i>PresencePenalty</i>.</div><div><br /></div><div>Add this line of code to <i>Program.cs</i> right above <i>var app = builder.Build();</i>:</div><div><br /></div><div><span style="color: #38761d; font-family: courier;">// OpenAIClient</span></div><div><span style="font-family: courier;">builder.Services.AddOpenAIClient(x => builder.Configuration.Bind(nameof(OpenAIClientConfiguration), x));</span></div><div><span style="font-family: courier;">builder.Services.AddScoped<AzureOpenAIService>();</span></div><h2 style="text-align: left;">Razor Page</h2><div>In the <i>Pages </i>folder, create razor pages named<i> CompletionPage.razor</i> with the following content:</div><div><br /></div><div><span style="font-family: courier;">@page "/completion"</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">@using AOAIServerSideBlazor.Data</span></div><div><span style="font-family: courier;">@using AzureOpenAIClient.Http</span></div><div><span style="font-family: courier;">@inject AzureOpenAIService azureOpenAIService </span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><PageTitle>Completion Example</PageTitle></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><h3>Completion Example</h3></span></div><div><span style="font-family: courier;"><p></span></div><div><span style="font-family: courier;"><InputTextArea @bind-Value=@TextValue Cols="100" Rows="10" /></span></div><div><span style="font-family: courier;"></p></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><button class="btn btn-primary" @onclick="CallCompletion">Call Completion</button></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">@code {</span></div><div><span style="font-family: courier;"> string TextValue = "Four score and seven years ago";</span></div><div><span style="font-family: courier;"> CompletionResponse? completionResponse;</span></div><div><span style="font-family: courier;"> </span></div><div><span style="font-family: courier;"> async Task CallCompletion()</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> if (TextValue != null)</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> completionResponse = await azureOpenAIService.GetTextCompletionResponse(TextValue, 500);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> if (completionResponse?.Choices.Count > 0)</span></div><div><span style="font-family: courier;"> {</span></div><div><span style="font-family: courier;"> TextValue = TextValue + completionResponse.Choices[0].Text;</span></div><div><span style="font-family: courier;"> StateHasChanged();</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"> }</span></div><div><span style="font-family: courier;">}</span></div><div><br /></div><div>The above code starts by declaring two variables: <i>completionResponse </i>of type <i>CompletionResponse </i>and <i>TextValue </i>of type <i>string </i>with an initial value of "Four score and seven years ago".</div><div><br /></div><div>The code block also contains an asynchronous method called <i>CallCompletion </i>that is executed when the "Call Completion" button is clicked.</div><div><br /></div><div>The <i>CallCompletion </i>method checks if the <i>TextValue </i>is not null. If it's not, it calls the <i>GetTextCompletionResponse </i>method on the <i>azureOpenAIService </i>instance, passing in <i>TextValue </i>and 500 as the <i>maxTokens </i>argument. The response from the method is stored in the <i>completionResponse </i>variable.</div><div><br /></div><div>After that, it checks if the <i>Choices </i>property of <i>Completion </i>has a count greater than 0. If it does, it appends the <i>Choices[0]</i> text to <i>TextValue </i>and calls the <i>StateHasChanged </i>method, which signals Blazor to update the UI.</div><div><br /></div><div>Add the following code to <i><nav></i> block in <i>NavMenu.razor</i> so that we have a menu item in the left navigation that we can access our<i> /completion</i> page:</div><div><br /></div><div><span style="font-family: courier;"><div class="nav-item px-3"></span></div><div><span style="font-family: courier;"> <NavLink class="nav-link" href="completion"></span></div><div><span style="font-family: courier;"> <span class="oi oi-list-rich" aria-hidden="true"></span> Completion</span></div><div><span style="font-family: courier;"> </NavLink></span></div><div><span style="font-family: courier;"></div></span></div><div><br /></div><div>Run the application by executing the following terminal command:</div><div><br /></div><div style="text-align: center;"><span style="font-family: courier;">dotnet watch</span></div><div><br /></div><div>The application will start in a browser. Click on the <i>Completion </i>link on the left-side navigation. </div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgzRnKN-aHhcLB8a-MoVIzVTcFSdMrlJikBxEtsf_TlomINYS5Or-Sy45jCprCGyFVmanh1NyGGV8hyGJntcv6Zc7t0x_IIoIgMO0b6_CAhVtxwoum2fC11g05-DE5WdXOu1JLU_-dn8lpMa326sM96od7GMurc_COx83l-SzlOdkQfFkp-JCOIYHEgHw" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="440" data-original-width="951" height="296" src="https://blogger.googleusercontent.com/img/a/AVvXsEgzRnKN-aHhcLB8a-MoVIzVTcFSdMrlJikBxEtsf_TlomINYS5Or-Sy45jCprCGyFVmanh1NyGGV8hyGJntcv6Zc7t0x_IIoIgMO0b6_CAhVtxwoum2fC11g05-DE5WdXOu1JLU_-dn8lpMa326sM96od7GMurc_COx83l-SzlOdkQfFkp-JCOIYHEgHw=w640-h296" width="640" /></a></div><br />In the above scenario, you start with a default statement “Four score and seven years ago”. Click on “Call Completion” and after about 20 seconds, you will experience CharGPT completing the statement.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEipyNiXvb1xOrAMj6novD3_-T8Xs6gy3Jlk6SQNGdpW5-mAcfdNvxA29qFtQ6U4L3B2rEwXXjiJGo2dOsVr3Ae_tgX_4DCDtpLlIJIi2kq8GaPF_TRbE3ISMyfBbWJ736DWnP7mOSbvethSHp2zmdxt_5ZH1QXP5qwMRxKOYaM7MbrtYH48Uj_s1CGHnQ" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="425" data-original-width="936" height="290" src="https://blogger.googleusercontent.com/img/a/AVvXsEipyNiXvb1xOrAMj6novD3_-T8Xs6gy3Jlk6SQNGdpW5-mAcfdNvxA29qFtQ6U4L3B2rEwXXjiJGo2dOsVr3Ae_tgX_4DCDtpLlIJIi2kq8GaPF_TRbE3ISMyfBbWJ736DWnP7mOSbvethSHp2zmdxt_5ZH1QXP5qwMRxKOYaM7MbrtYH48Uj_s1CGHnQ=w640-h290" width="640" /></a></div><br />This tutorial should help you explore all kinds of ways to incorporate the power of ChatGPT into your applications through the Azure OpenAPI service.</div><br />References:</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">https://blazorhelpwebsite.com/ViewBlogPost/2065<br /></div></div>Medhat Elmasryhttp://www.blogger.com/profile/12743954622889282997noreply@blogger.com0