[{"data":1,"prerenderedAt":1450},["ShallowReactive",2],{"docs-navigation":3,"doc-\u002Fdev\u002Fcsharp\u002Fservices-and-helpers":229,"surround-\u002Fdev\u002Fcsharp\u002Fservices-and-helpers":1248,"notes-index":1253},[4,88,171,182,192,203,218],{"title":5,"path":6,"stem":7,"children":8},"Dev","\u002Fdev","1.dev",[9,12,26,36,50,56,78],{"title":10,"path":6,"stem":11},"Development","1.dev\u002Findex",{"title":13,"path":14,"stem":15,"children":16},"Laravel","\u002Fdev\u002Flaravel","1.dev\u002F1.laravel\u002Findex",[17,18,22],{"title":13,"path":14,"stem":15},{"title":19,"path":20,"stem":21},"Eloquent Relationships","\u002Fdev\u002Flaravel\u002Feloquent-relationships","1.dev\u002F1.laravel\u002F1.eloquent-relationships",{"title":23,"path":24,"stem":25},"Laravel Homestead","\u002Fdev\u002Flaravel\u002Flaravel-homestead","1.dev\u002F1.laravel\u002F2.laravel-homestead",{"title":27,"path":28,"stem":29,"children":30},"Bash","\u002Fdev\u002Fbash","1.dev\u002F13.bash\u002Findex",[31,32],{"title":27,"path":28,"stem":29},{"title":33,"path":34,"stem":35},"My Bash RC","\u002Fdev\u002Fbash\u002Fbash-rc","1.dev\u002F13.bash\u002F2.bash-rc",{"title":37,"path":38,"stem":39,"children":40},"Clean Code","\u002Fdev\u002Fclean-code","1.dev\u002F15.clean-code\u002Findex",[41,42,46],{"title":37,"path":38,"stem":39},{"title":43,"path":44,"stem":45},"Naming Things","\u002Fdev\u002Fclean-code\u002Fnaming-things","1.dev\u002F15.clean-code\u002F1.naming-things",{"title":47,"path":48,"stem":49},"Services Return, Callers Present","\u002Fdev\u002Fclean-code\u002Fservices-return-callers-present","1.dev\u002F15.clean-code\u002F2.services-return-callers-present",{"title":51,"path":52,"stem":53,"children":54},"AI Prompting","\u002Fdev\u002Fai-prompting","1.dev\u002F17.ai-prompting\u002Findex",[55],{"title":51,"path":52,"stem":53},{"title":57,"path":58,"stem":59,"children":60},"C#","\u002Fdev\u002Fcsharp","1.dev\u002F2.csharp\u002Findex",[61,62,66,70,74],{"title":57,"path":58,"stem":59},{"title":63,"path":64,"stem":65},"C# Basics","\u002Fdev\u002Fcsharp\u002Fcsharp-basics","1.dev\u002F2.csharp\u002F1.csharp-basics",{"title":67,"path":68,"stem":69},"Classes & Models","\u002Fdev\u002Fcsharp\u002Fclasses-and-models","1.dev\u002F2.csharp\u002F2.classes-and-models",{"title":71,"path":72,"stem":73},"Services & Helpers","\u002Fdev\u002Fcsharp\u002Fservices-and-helpers","1.dev\u002F2.csharp\u002F3.services-and-helpers",{"title":75,"path":76,"stem":77},"Project Structure","\u002Fdev\u002Fcsharp\u002Fproject-structure","1.dev\u002F2.csharp\u002F4.project-structure",{"title":79,"path":80,"stem":81,"children":82},"Git","\u002Fdev\u002Fgit","1.dev\u002F9.git\u002Findex",[83,84],{"title":79,"path":80,"stem":81},{"title":85,"path":86,"stem":87},"Git Cheat Sheet","\u002Fdev\u002Fgit\u002Fgit-cheat-sheet","1.dev\u002F9.git\u002F1.git-cheat-sheet",{"title":89,"path":90,"stem":91,"children":92},"Teaching","\u002Fteaching","2.teaching",[93,95],{"title":89,"path":90,"stem":94},"2.teaching\u002Findex",{"title":96,"path":97,"stem":98,"children":99},"AppsDev","\u002Fteaching\u002Fapps-dev","2.teaching\u002F1.apps-dev\u002Findex",[100,101,119,137,147,161],{"title":96,"path":97,"stem":98},{"title":102,"path":103,"stem":104,"children":105},"Lesson Plans","\u002Fteaching\u002Fapps-dev\u002Flesson-plans","2.teaching\u002F1.apps-dev\u002F1.lesson-plans\u002Findex",[106,107,111,115],{"title":102,"path":103,"stem":104},{"title":108,"path":109,"stem":110},"Coding with AI","\u002Fteaching\u002Fapps-dev\u002Flesson-plans\u002Fcoding-with-ai","2.teaching\u002F1.apps-dev\u002F1.lesson-plans\u002F1.coding-with-ai",{"title":112,"path":113,"stem":114},"Coding with AI: Prompts & Reading JavaScript","\u002Fteaching\u002Fapps-dev\u002Flesson-plans\u002Fprompts-and-reading-js","2.teaching\u002F1.apps-dev\u002F1.lesson-plans\u002F2.prompts-and-reading-js",{"title":116,"path":117,"stem":118},"JavaScript for Beginners","\u002Fteaching\u002Fapps-dev\u002Flesson-plans\u002Fjavascript-for-beginners","2.teaching\u002F1.apps-dev\u002F1.lesson-plans\u002F3.javascript-for-beginners",{"title":120,"path":121,"stem":122,"children":123},"Activities","\u002Fteaching\u002Fapps-dev\u002Factivities","2.teaching\u002F1.apps-dev\u002F2.activities\u002Findex",[124,125,129,133],{"title":120,"path":121,"stem":122},{"title":126,"path":127,"stem":128},"Dynamic Portfolio Website","\u002Fteaching\u002Fapps-dev\u002Factivities\u002Fdynamic-portfolio-website","2.teaching\u002F1.apps-dev\u002F2.activities\u002F2.dynamic-portfolio-website",{"title":130,"path":131,"stem":132},"Mini Product Store","\u002Fteaching\u002Fapps-dev\u002Factivities\u002Fmini-product-store","2.teaching\u002F1.apps-dev\u002F2.activities\u002F3.mini-product-store",{"title":134,"path":135,"stem":136},"Semester Project","\u002Fteaching\u002Fapps-dev\u002Factivities\u002Fsemester-project","2.teaching\u002F1.apps-dev\u002F2.activities\u002F4.semester-project",{"title":138,"path":139,"stem":140,"children":141},"Exercises","\u002Fteaching\u002Fapps-dev\u002Fexercises","2.teaching\u002F1.apps-dev\u002F4.exercises\u002Findex",[142,143],{"title":138,"path":139,"stem":140},{"title":144,"path":145,"stem":146},"Read & Predict","\u002Fteaching\u002Fapps-dev\u002Fexercises\u002Fread-and-predict","2.teaching\u002F1.apps-dev\u002F4.exercises\u002F1.read-and-predict",{"title":148,"path":149,"stem":150,"children":151},"AI Prompts","\u002Fteaching\u002Fapps-dev\u002Fai-prompts","2.teaching\u002F1.apps-dev\u002F7.ai-prompts\u002Findex",[152,153,157],{"title":148,"path":149,"stem":150},{"title":154,"path":155,"stem":156},"Prompt Flow","\u002Fteaching\u002Fapps-dev\u002Fai-prompts\u002Fbasic-prompting","2.teaching\u002F1.apps-dev\u002F7.ai-prompts\u002F1.basic-prompting",{"title":158,"path":159,"stem":160},"Using OpenCode CLI Instead of Copy-Pasting from ChatGPT","\u002Fteaching\u002Fapps-dev\u002Fai-prompts\u002Fopencode-cli","2.teaching\u002F1.apps-dev\u002F7.ai-prompts\u002F2.opencode-cli",{"title":162,"path":163,"stem":164,"children":165},"Cheat Sheets","\u002Fteaching\u002Fapps-dev\u002Fcheat-sheets","2.teaching\u002F1.apps-dev\u002F8.cheat-sheets\u002Findex",[166,167],{"title":162,"path":163,"stem":164},{"title":168,"path":169,"stem":170},"How to Collaborate on GitHub","\u002Fteaching\u002Fapps-dev\u002Fcheat-sheets\u002Fgithub-collaboration","2.teaching\u002F1.apps-dev\u002F8.cheat-sheets\u002F1.github-collaboration",{"title":172,"path":173,"stem":174,"children":175},"Snippets","\u002Fsnippets","3.snippets",[176,178],{"title":172,"path":173,"stem":177},"3.snippets\u002Findex",{"title":179,"path":180,"stem":181},"Debounce and Throttle","\u002Fsnippets\u002Fdebounce-and-throttle","3.snippets\u002F1.debounce-and-throttle",{"title":162,"path":183,"stem":184,"children":185},"\u002Fcheat-sheets","4.cheat-sheets",[186,188],{"title":162,"path":183,"stem":187},"4.cheat-sheets\u002Findex",{"title":189,"path":190,"stem":191},"Vim Cheat Sheet","\u002Fcheat-sheets\u002Fvim-cheat-sheet","4.cheat-sheets\u002F1.vim-cheat-sheet",{"title":193,"path":194,"stem":195,"children":196},"Templates","\u002Ftemplates","5.templates",[197,199],{"title":193,"path":194,"stem":198},"5.templates\u002Findex",{"title":200,"path":201,"stem":202},"Pull Request Template","\u002Ftemplates\u002Fpull-request-template","5.templates\u002F1.pull-request-template",{"title":204,"path":205,"stem":206,"children":207},"Meta","\u002Fmeta","6.meta",[208,210,214],{"title":204,"path":205,"stem":209},"6.meta\u002Findex",{"title":211,"path":212,"stem":213},"Authoring Guide","\u002Fmeta\u002Fauthoring-guide","6.meta\u002F1.authoring-guide",{"title":215,"path":216,"stem":217},"Markdown Showcase","\u002Fmeta\u002Fmarkdown-showcase","6.meta\u002F2.markdown-showcase",{"title":219,"path":220,"stem":221,"children":222},"Troubleshooting","\u002Ftroubleshooting","7.troubleshooting",[223,225],{"title":219,"path":220,"stem":224},"7.troubleshooting\u002Findex",{"title":226,"path":227,"stem":228},"Fixing CORS Errors","\u002Ftroubleshooting\u002Fcors-errors","7.troubleshooting\u002F1.cors-errors",{"id":230,"title":71,"body":231,"category":1230,"createdAt":1231,"description":1232,"draft":1233,"extension":1234,"featured":1233,"keywords":1235,"links":1241,"meta":1242,"navigation":301,"path":72,"readingTime":305,"seo":1243,"stem":73,"tags":1244,"updatedAt":1231,"__hash__":1247},"docs\u002F1.dev\u002F2.csharp\u002F3.services-and-helpers.md",{"type":232,"value":233,"toc":1223},"minimark",[234,257,262,265,553,563,825,847,851,866,976,1029,1033,1098,1102,1112,1178,1205,1209,1219],[235,236,237,238,242,243,247,248,252,253,256],"p",{},"You already write services in Laravel — the C# version is nearly the same shape.\nThe two things to learn are how another file becomes ",[239,240,241],"em",{},"visible"," (namespaces +\n",[244,245,246],"code",{},"using",") and when to make it a ",[249,250,251],"strong",{},"static helper"," vs an ",[249,254,255],{},"instance service",".",[258,259,261],"h2",{"id":260},"a-service-in-its-own-file","A service in its own file",[235,263,264],{},"Say you have a login service. It lives in its own file, in a namespace:",[266,267,273],"pre",{"className":268,"code":269,"filename":270,"language":271,"meta":272,"style":272},"language-csharp shiki shiki-themes material-theme-lighter vitesse-light vitesse-dark","using MyApp.Models;\n\nnamespace MyApp.Services;\n\npublic class LoginService\n{\n    private readonly List\u003CUser> _users;\n\n    public LoginService(List\u003CUser> users)\n    {\n        _users = users;\n    }\n\n    \u002F\u002F Return a RESULT — let the caller decide how to present it.\n    public User? Login(string username, string password)\n    {\n        return _users.FirstOrDefault(\n            u => u.Username == username && u.Password == password\n        );\n    }\n}\n","Services\u002FLoginService.cs","csharp","",[244,274,275,296,303,319,324,338,344,371,376,404,410,424,430,435,442,476,481,498,536,542,547],{"__ignoreMap":272},[276,277,280,283,287,290,293],"span",{"class":278,"line":279},"line",1,[276,281,246],{"class":282},"scgAs",[276,284,286],{"class":285},"sbc-P"," MyApp",[276,288,256],{"class":289},"soVBu",[276,291,292],{"class":285},"Models",[276,294,295],{"class":289},";\n",[276,297,299],{"class":278,"line":298},2,[276,300,302],{"emptyLinePlaceholder":301},true,"\n",[276,304,306,310,312,314,317],{"class":278,"line":305},3,[276,307,309],{"class":308},"s23sZ","namespace",[276,311,286],{"class":285},[276,313,256],{"class":289},[276,315,316],{"class":285},"Services",[276,318,295],{"class":289},[276,320,322],{"class":278,"line":321},4,[276,323,302],{"emptyLinePlaceholder":301},[276,325,327,331,334],{"class":278,"line":326},5,[276,328,330],{"class":329},"s5Kfy","public",[276,332,333],{"class":308}," class",[276,335,337],{"class":336},"sD-vU"," LoginService\n",[276,339,341],{"class":278,"line":340},6,[276,342,343],{"class":289},"{\n",[276,345,347,350,353,356,359,362,365,369],{"class":278,"line":346},7,[276,348,349],{"class":329},"    private",[276,351,352],{"class":329}," readonly",[276,354,355],{"class":336}," List",[276,357,358],{"class":289},"\u003C",[276,360,361],{"class":336},"User",[276,363,364],{"class":289},">",[276,366,368],{"class":367},"sYn-s"," _users",[276,370,295],{"class":289},[276,372,374],{"class":278,"line":373},8,[276,375,302],{"emptyLinePlaceholder":301},[276,377,379,382,386,389,392,394,396,398,401],{"class":278,"line":378},9,[276,380,381],{"class":329},"    public",[276,383,385],{"class":384},"sljsM"," LoginService",[276,387,388],{"class":289},"(",[276,390,391],{"class":336},"List",[276,393,358],{"class":289},[276,395,361],{"class":336},[276,397,364],{"class":289},[276,399,400],{"class":367}," users",[276,402,403],{"class":289},")\n",[276,405,407],{"class":278,"line":406},10,[276,408,409],{"class":289},"    {\n",[276,411,413,417,420,422],{"class":278,"line":412},11,[276,414,416],{"class":415},"sSC40","        _users",[276,418,419],{"class":289}," =",[276,421,400],{"class":415},[276,423,295],{"class":289},[276,425,427],{"class":278,"line":426},12,[276,428,429],{"class":289},"    }\n",[276,431,433],{"class":278,"line":432},13,[276,434,302],{"emptyLinePlaceholder":301},[276,436,438],{"class":278,"line":437},14,[276,439,441],{"class":440},"s9Tkl","    \u002F\u002F Return a RESULT — let the caller decide how to present it.\n",[276,443,445,447,450,453,456,458,462,465,468,471,474],{"class":278,"line":444},15,[276,446,381],{"class":329},[276,448,449],{"class":336}," User",[276,451,452],{"class":289},"?",[276,454,455],{"class":384}," Login",[276,457,388],{"class":289},[276,459,461],{"class":460},"s8XtY","string",[276,463,464],{"class":367}," username",[276,466,467],{"class":289},",",[276,469,470],{"class":460}," string",[276,472,473],{"class":367}," password",[276,475,403],{"class":289},[276,477,479],{"class":278,"line":478},16,[276,480,409],{"class":289},[276,482,484,488,490,492,495],{"class":278,"line":483},17,[276,485,487],{"class":486},"siDh9","        return",[276,489,368],{"class":415},[276,491,256],{"class":289},[276,493,494],{"class":384},"FirstOrDefault",[276,496,497],{"class":289},"(\n",[276,499,501,504,508,511,513,516,519,521,524,526,528,531,533],{"class":278,"line":500},18,[276,502,503],{"class":367},"            u",[276,505,507],{"class":506},"sVsLi"," =>",[276,509,510],{"class":415}," u",[276,512,256],{"class":289},[276,514,515],{"class":415},"Username",[276,517,518],{"class":506}," ==",[276,520,464],{"class":415},[276,522,523],{"class":506}," &&",[276,525,510],{"class":415},[276,527,256],{"class":289},[276,529,530],{"class":415},"Password",[276,532,518],{"class":506},[276,534,535],{"class":415}," password\n",[276,537,539],{"class":278,"line":538},19,[276,540,541],{"class":289},"        );\n",[276,543,545],{"class":278,"line":544},20,[276,546,429],{"class":289},[276,548,550],{"class":278,"line":549},21,[276,551,552],{"class":289},"}\n",[235,554,555,556,559,560,562],{},"Use it from ",[244,557,558],{},"Program.cs"," — ",[244,561,246],{}," makes the namespace visible:",[266,564,566],{"className":268,"code":565,"filename":558,"language":271,"meta":272,"style":272},"using MyApp.Models;\nusing MyApp.Services;\n\nvar users = new List\u003CUser>\n{\n    new User { Username = \"ada\", Password = \"secret\" }\n};\n\nvar loginService = new LoginService(users);\n\nvar user = loginService.Login(\"ada\", \"secret\");\n\n\u002F\u002F The CALLER handles presentation, not the service:\nif (user is not null)\n    Console.WriteLine($\"Welcome, {user.Username}!\");\nelse\n    Console.WriteLine(\"Invalid credentials.\");\n",[244,567,568,580,592,596,617,621,664,669,673,694,698,732,736,741,763,801,806],{"__ignoreMap":272},[276,569,570,572,574,576,578],{"class":278,"line":279},[276,571,246],{"class":282},[276,573,286],{"class":285},[276,575,256],{"class":289},[276,577,292],{"class":285},[276,579,295],{"class":289},[276,581,582,584,586,588,590],{"class":278,"line":298},[276,583,246],{"class":282},[276,585,286],{"class":285},[276,587,256],{"class":289},[276,589,316],{"class":285},[276,591,295],{"class":289},[276,593,594],{"class":278,"line":305},[276,595,302],{"emptyLinePlaceholder":301},[276,597,598,601,603,605,608,610,612,614],{"class":278,"line":321},[276,599,600],{"class":308},"var",[276,602,400],{"class":367},[276,604,419],{"class":289},[276,606,607],{"class":506}," new",[276,609,355],{"class":336},[276,611,358],{"class":289},[276,613,361],{"class":336},[276,615,616],{"class":289},">\n",[276,618,619],{"class":278,"line":326},[276,620,343],{"class":289},[276,622,623,626,628,631,634,636,640,644,647,649,652,654,656,659,661],{"class":278,"line":340},[276,624,625],{"class":506},"    new",[276,627,449],{"class":336},[276,629,630],{"class":289}," {",[276,632,633],{"class":415}," Username",[276,635,419],{"class":289},[276,637,639],{"class":638},"sbYkP"," \"",[276,641,643],{"class":642},"sTbE_","ada",[276,645,646],{"class":638},"\"",[276,648,467],{"class":289},[276,650,651],{"class":415}," Password",[276,653,419],{"class":289},[276,655,639],{"class":638},[276,657,658],{"class":642},"secret",[276,660,646],{"class":638},[276,662,663],{"class":289}," }\n",[276,665,666],{"class":278,"line":346},[276,667,668],{"class":289},"};\n",[276,670,671],{"class":278,"line":373},[276,672,302],{"emptyLinePlaceholder":301},[276,674,675,677,680,682,684,686,688,691],{"class":278,"line":378},[276,676,600],{"class":308},[276,678,679],{"class":367}," loginService",[276,681,419],{"class":289},[276,683,607],{"class":506},[276,685,385],{"class":336},[276,687,388],{"class":289},[276,689,690],{"class":415},"users",[276,692,693],{"class":289},");\n",[276,695,696],{"class":278,"line":406},[276,697,302],{"emptyLinePlaceholder":301},[276,699,700,702,705,707,709,711,714,716,718,720,722,724,726,728,730],{"class":278,"line":412},[276,701,600],{"class":308},[276,703,704],{"class":367}," user",[276,706,419],{"class":289},[276,708,679],{"class":415},[276,710,256],{"class":289},[276,712,713],{"class":384},"Login",[276,715,388],{"class":289},[276,717,646],{"class":638},[276,719,643],{"class":642},[276,721,646],{"class":638},[276,723,467],{"class":289},[276,725,639],{"class":638},[276,727,658],{"class":642},[276,729,646],{"class":638},[276,731,693],{"class":289},[276,733,734],{"class":278,"line":426},[276,735,302],{"emptyLinePlaceholder":301},[276,737,738],{"class":278,"line":432},[276,739,740],{"class":440},"\u002F\u002F The CALLER handles presentation, not the service:\n",[276,742,743,746,749,752,755,758,761],{"class":278,"line":437},[276,744,745],{"class":486},"if",[276,747,748],{"class":289}," (",[276,750,751],{"class":415},"user",[276,753,754],{"class":506}," is",[276,756,757],{"class":506}," not",[276,759,760],{"class":506}," null",[276,762,403],{"class":289},[276,764,765,768,770,773,775,778,781,784,787,789,791,794,797,799],{"class":278,"line":444},[276,766,767],{"class":415},"    Console",[276,769,256],{"class":289},[276,771,772],{"class":384},"WriteLine",[276,774,388],{"class":289},[276,776,777],{"class":638},"$\"",[276,779,780],{"class":642},"Welcome, ",[276,782,783],{"class":289},"{",[276,785,751],{"class":786},"su_V2",[276,788,256],{"class":289},[276,790,515],{"class":786},[276,792,793],{"class":289},"}",[276,795,796],{"class":642},"!",[276,798,646],{"class":638},[276,800,693],{"class":289},[276,802,803],{"class":278,"line":478},[276,804,805],{"class":486},"else\n",[276,807,808,810,812,814,816,818,821,823],{"class":278,"line":483},[276,809,767],{"class":415},[276,811,256],{"class":289},[276,813,772],{"class":384},[276,815,388],{"class":289},[276,817,646],{"class":638},[276,819,820],{"class":642},"Invalid credentials.",[276,822,646],{"class":638},[276,824,693],{"class":289},[826,827,828],"warning",{},[235,829,830,831,834,835,838,839,842,843,256],{},"Don't put ",[244,832,833],{},"Console.WriteLine"," ",[249,836,837],{},"inside"," the service. A service that returns a\n",[244,840,841],{},"User?"," (or a success\u002Ffailure result) can be reused from a console app, a web\nAPI, or a test. A service that prints to the console can only ever be a console\napp. Same rule as Laravel: the service returns data, the controller renders it.\nSee ",[844,845,846],"a",{"href":48},"Services return, callers present",[258,848,850],{"id":849},"static-helper-utility-functions","Static helper (utility functions)",[235,852,853,854,857,858,861,862,865],{},"A ",[249,855,856],{},"helper"," is stateless — a bag of pure functions, like a Laravel helper file.\nMake the class ",[244,859,860],{},"static"," so you call it directly without ",[244,863,864],{},"new",":",[266,867,870],{"className":268,"code":868,"filename":869,"language":271,"meta":272,"style":272},"namespace MyApp.Helpers;\n\npublic static class TextHelper\n{\n    public static string Slugify(string input)\n    {\n        return input.Trim().ToLower().Replace(\" \", \"-\");\n    }\n}\n","Helpers\u002FTextHelper.cs",[244,871,872,885,889,901,905,925,929,968,972],{"__ignoreMap":272},[276,873,874,876,878,880,883],{"class":278,"line":279},[276,875,309],{"class":308},[276,877,286],{"class":285},[276,879,256],{"class":289},[276,881,882],{"class":285},"Helpers",[276,884,295],{"class":289},[276,886,887],{"class":278,"line":298},[276,888,302],{"emptyLinePlaceholder":301},[276,890,891,893,896,898],{"class":278,"line":305},[276,892,330],{"class":329},[276,894,895],{"class":329}," static",[276,897,333],{"class":308},[276,899,900],{"class":336}," TextHelper\n",[276,902,903],{"class":278,"line":321},[276,904,343],{"class":289},[276,906,907,909,911,913,916,918,920,923],{"class":278,"line":326},[276,908,381],{"class":329},[276,910,895],{"class":329},[276,912,470],{"class":460},[276,914,915],{"class":384}," Slugify",[276,917,388],{"class":289},[276,919,461],{"class":460},[276,921,922],{"class":367}," input",[276,924,403],{"class":289},[276,926,927],{"class":278,"line":340},[276,928,409],{"class":289},[276,930,931,933,935,937,940,943,946,948,951,953,955,957,959,961,964,966],{"class":278,"line":346},[276,932,487],{"class":486},[276,934,922],{"class":415},[276,936,256],{"class":289},[276,938,939],{"class":384},"Trim",[276,941,942],{"class":289},"().",[276,944,945],{"class":384},"ToLower",[276,947,942],{"class":289},[276,949,950],{"class":384},"Replace",[276,952,388],{"class":289},[276,954,646],{"class":638},[276,956,639],{"class":638},[276,958,467],{"class":289},[276,960,639],{"class":638},[276,962,963],{"class":642},"-",[276,965,646],{"class":638},[276,967,693],{"class":289},[276,969,970],{"class":278,"line":373},[276,971,429],{"class":289},[276,973,974],{"class":278,"line":378},[276,975,552],{"class":289},[266,977,979],{"className":268,"code":978,"filename":558,"language":271,"meta":272,"style":272},"using MyApp.Helpers;\n\nvar slug = TextHelper.Slugify(\"Hello World\");   \u002F\u002F \"hello-world\"\n",[244,980,981,993,997],{"__ignoreMap":272},[276,982,983,985,987,989,991],{"class":278,"line":279},[276,984,246],{"class":282},[276,986,286],{"class":285},[276,988,256],{"class":289},[276,990,882],{"class":285},[276,992,295],{"class":289},[276,994,995],{"class":278,"line":298},[276,996,302],{"emptyLinePlaceholder":301},[276,998,999,1001,1004,1006,1009,1011,1014,1016,1018,1021,1023,1026],{"class":278,"line":305},[276,1000,600],{"class":308},[276,1002,1003],{"class":367}," slug",[276,1005,419],{"class":289},[276,1007,1008],{"class":415}," TextHelper",[276,1010,256],{"class":289},[276,1012,1013],{"class":384},"Slugify",[276,1015,388],{"class":289},[276,1017,646],{"class":638},[276,1019,1020],{"class":642},"Hello World",[276,1022,646],{"class":638},[276,1024,1025],{"class":289},");",[276,1027,1028],{"class":440},"   \u002F\u002F \"hello-world\"\n",[258,1030,1032],{"id":1031},"service-vs-helper-which-one","Service vs helper — which one?",[1034,1035,1036,1054],"table",{},[1037,1038,1039],"thead",{},[1040,1041,1042,1050],"tr",{},[1043,1044,1045,1046,1049],"th",{},"Use a ",[249,1047,1048],{},"service"," (instance)",[1043,1051,1045,1052],{},[249,1053,251],{},[1055,1056,1057,1066,1074,1086],"tbody",{},[1040,1058,1059,1063],{},[1060,1061,1062],"td",{},"Holds state or dependencies (a user list, a DB, config)",[1060,1064,1065],{},"Pure input → output, no state",[1040,1067,1068,1071],{},[1060,1069,1070],{},"You want to swap\u002Fmock it in tests",[1060,1072,1073],{},"Never needs mocking",[1040,1075,1076,1081],{},[1060,1077,1078],{},[244,1079,1080],{},"new LoginService(users)",[1060,1082,1083],{},[244,1084,1085],{},"TextHelper.Slugify(...)",[1040,1087,1088,1091],{},[1060,1089,1090],{},"Laravel: an injected service class",[1060,1092,1093,1094,1097],{},"Laravel: a ",[244,1095,1096],{},"helpers.php"," function",[258,1099,1101],{"id":1100},"the-grown-up-version-dependency-injection","The grown-up version: dependency injection",[235,1103,1104,1105,1107,1108,1111],{},"Creating services with ",[244,1106,864],{}," by hand is fine for a small console app. Real .NET\napps register services in a ",[249,1109,1110],{},"container"," and let it hand them to you — exactly\nlike Laravel's service container binding:",[266,1113,1115],{"className":268,"code":1114,"language":271,"meta":272,"style":272},"\u002F\u002F In a full app's startup:\nbuilder.Services.AddScoped\u003CLoginService>();\n\n\u002F\u002F Then any class can just ask for it in its constructor:\npublic class LoginController(LoginService loginService) { \u002F* ... *\u002F }\n",[244,1116,1117,1122,1144,1148,1153],{"__ignoreMap":272},[276,1118,1119],{"class":278,"line":279},[276,1120,1121],{"class":440},"\u002F\u002F In a full app's startup:\n",[276,1123,1124,1127,1129,1131,1133,1136,1138,1141],{"class":278,"line":298},[276,1125,1126],{"class":415},"builder",[276,1128,256],{"class":289},[276,1130,316],{"class":415},[276,1132,256],{"class":289},[276,1134,1135],{"class":384},"AddScoped",[276,1137,358],{"class":289},[276,1139,1140],{"class":336},"LoginService",[276,1142,1143],{"class":289},">();\n",[276,1145,1146],{"class":278,"line":305},[276,1147,302],{"emptyLinePlaceholder":301},[276,1149,1150],{"class":278,"line":321},[276,1151,1152],{"class":440},"\u002F\u002F Then any class can just ask for it in its constructor:\n",[276,1154,1155,1157,1159,1162,1164,1166,1168,1171,1173,1176],{"class":278,"line":326},[276,1156,330],{"class":329},[276,1158,333],{"class":308},[276,1160,1161],{"class":336}," LoginController",[276,1163,388],{"class":289},[276,1165,1140],{"class":336},[276,1167,679],{"class":367},[276,1169,1170],{"class":289},")",[276,1172,630],{"class":289},[276,1174,1175],{"class":440}," \u002F* ... *\u002F",[276,1177,663],{"class":289},[1179,1180,1181],"tip",{},[235,1182,1183,1184,1186,1187,1189,1190,1189,1193,1196,1197,1200,1201,1204],{},"You don't need DI yet for a terminal app — ",[244,1185,864],{}," is honest and simple. But\nrecognise it when you see it: ",[244,1188,1135],{}," \u002F ",[244,1191,1192],{},"AddSingleton",[244,1194,1195],{},"AddTransient"," is\n",[244,1198,1199],{},".NET"," doing what ",[244,1202,1203],{},"$this->app->bind()"," does in Laravel. Reach for it once you\nhave more than a handful of services wiring each other up.",[258,1206,1208],{"id":1207},"next","Next",[235,1210,1211,1212,1215,1216,256],{},"See how namespaces, folders and the ",[244,1213,1214],{},"dotnet"," CLI tie all these files together → ",[844,1217,1218],{"href":76},"Project structure",[1220,1221,1222],"style",{},"html pre.shiki code .scgAs, html code.shiki .scgAs{--shiki-light:#F76D47;--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .sbc-P, html code.shiki .sbc-P{--shiki-light:#90A4AE;--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .soVBu, html code.shiki .soVBu{--shiki-light:#39ADB5;--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .s23sZ, html code.shiki .s23sZ{--shiki-light:#E2931D;--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s5Kfy, html code.shiki .s5Kfy{--shiki-light:#9C3EDA;--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .sD-vU, html code.shiki .sD-vU{--shiki-light:#E2931D;--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .sYn-s, html code.shiki .sYn-s{--shiki-light:#E2931D;--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .sljsM, html code.shiki .sljsM{--shiki-light:#6182B8;--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .sSC40, html code.shiki .sSC40{--shiki-light:#90A4AE;--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .s9Tkl, html code.shiki .s9Tkl{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#A0ADA0;--shiki-default-font-style:inherit;--shiki-dark:#758575DD;--shiki-dark-font-style:inherit}html pre.shiki code .s8XtY, html code.shiki .s8XtY{--shiki-light:#39ADB5;--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .siDh9, html code.shiki .siDh9{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#1E754F;--shiki-default-font-style:inherit;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit}html pre.shiki code .sVsLi, html code.shiki .sVsLi{--shiki-light:#39ADB5;--shiki-default:#AB5959;--shiki-dark:#CB7676}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbYkP, html code.shiki .sbYkP{--shiki-light:#39ADB5;--shiki-default:#B5695977;--shiki-dark:#C98A7D77}html pre.shiki code .sTbE_, html code.shiki .sTbE_{--shiki-light:#91B859;--shiki-default:#B56959;--shiki-dark:#C98A7D}html pre.shiki code .su_V2, html code.shiki .su_V2{--shiki-light:#90A4AE;--shiki-default:#B56959;--shiki-dark:#C98A7D}",{"title":272,"searchDepth":305,"depth":305,"links":1224},[1225,1226,1227,1228,1229],{"id":260,"depth":298,"text":261},{"id":849,"depth":298,"text":850},{"id":1031,"depth":298,"text":1032},{"id":1100,"depth":298,"text":1101},{"id":1207,"depth":298,"text":1208},null,"2026-07-19","Put code in another file — a service or a static helper — and call it from anywhere, the C# way.",false,"md",[1236,1237,1238,309,246,1239,1240],"c# service","c# helper","static class","dependency injection","login service",[],{},{"title":71,"description":1232},[271,1214,1245,1246],"services","architecture","SxtkYF0HI5JcoCy84vbxGSmD3u6h_qyAXNSMhMDXIn0",[1249,1251],{"title":67,"path":68,"stem":69,"description":1250,"children":-1},"Create a class, give it properties and a constructor, and use it — the C# version of a Laravel model.",{"title":75,"path":76,"stem":77,"description":1252,"children":-1},"How C# files, namespaces, folders and the dotnet CLI fit together into a running app.",[1254,1264,1271,1274,1282,1287,1292,1296,1298,1300,1303,1311,1317,1320,1326,1329,1335,1338,1341,1346,1349,1352,1358,1362,1367,1370,1376,1380,1383,1388,1391,1394,1397,1400,1402,1408,1411,1416,1419,1423,1426,1433,1438,1441,1447],{"path":135,"title":134,"description":1255,"tags":1256,"category":120,"featured":1233,"updatedAt":1263,"createdAt":1263,"readingTime":426,"draft":1233},"Teams of four build one frontend application over the whole semester using AI, in a single shared GitHub repository where everyone contributes.",[1257,1258,1259,1260,1261,1262],"teaching","activity","capstone","github","ai","frontend","2026-08-01",{"path":169,"title":168,"description":1265,"tags":1266,"category":162,"featured":1233,"updatedAt":1263,"createdAt":1263,"readingTime":321,"draft":1233},"Create one repository, push your project, and add your teammates as collaborators so the whole team can work on the same code.",[1260,1267,1268,1269,1270],"git","collaboration","teamwork","setup",{"path":163,"title":162,"description":1272,"tags":1273,"category":162,"featured":1233,"updatedAt":1263,"createdAt":1263,"readingTime":279,"draft":1233},"Step-by-step references students can follow on their own during class.",[],{"path":159,"title":158,"description":1275,"tags":1276,"category":148,"featured":1233,"updatedAt":1281,"createdAt":1281,"readingTime":305,"draft":1233},"Install and configure the OpenCode CLI so AI writes code directly into your project folder instead of you copying it from a chat window.",[1261,1277,1278,1279,1280],"opencode","cli","tooling","workflow","2026-07-25",{"path":48,"title":47,"description":1283,"tags":1284,"category":37,"featured":1233,"updatedAt":1231,"createdAt":1231,"readingTime":298,"draft":1233},"A service should return a result and let the caller decide how to show it — the single most useful separation-of-concerns rule.",[1285,1246,1286,1245],"clean-code","separation-of-concerns",{"path":64,"title":63,"description":1288,"tags":1289,"category":1230,"featured":1233,"updatedAt":1231,"createdAt":1231,"readingTime":305,"draft":1233},"Types, variables, control flow and how to write a function (method) in C#, mapped from PHP.",[271,1214,1290,1291],"basics","functions",{"path":68,"title":67,"description":1250,"tags":1293,"category":1230,"featured":1233,"updatedAt":1231,"createdAt":1231,"readingTime":305,"draft":1233},[271,1214,1294,1295],"classes","models",{"path":72,"title":71,"description":1232,"tags":1297,"category":1230,"featured":1233,"updatedAt":1231,"createdAt":1231,"readingTime":305,"draft":1233},[271,1214,1245,1246],{"path":76,"title":75,"description":1252,"tags":1299,"category":1230,"featured":1233,"updatedAt":1231,"createdAt":1231,"readingTime":305,"draft":1233},[271,1214,1279,1246],{"path":58,"title":57,"description":1301,"tags":1302,"category":1230,"featured":1233,"updatedAt":1231,"createdAt":1231,"readingTime":279,"draft":1233},"Learning C# and .NET from a Laravel\u002FPHP background — the fundamentals, mapped to what you already know.",[271,1214,1290],{"path":20,"title":19,"description":1304,"tags":1305,"category":13,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"The core Eloquent relationship types with examples and the N+1 trap to avoid.",[1306,1307,1308,1309],"laravel","eloquent","database","orm","2026-07-17",{"path":24,"title":23,"description":1312,"tags":1313,"category":13,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"My quick reference for setting up and using Laravel Homestead during development.",[1306,1314,1315,1316],"homestead","vagrant","virtualbox",{"path":14,"title":13,"description":1318,"tags":1319,"category":13,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"The PHP framework for web artisans — Eloquent, routing, queues and more.",[],{"path":34,"title":33,"description":1321,"tags":1322,"category":27,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":298,"draft":1233},"Personal Bash aliases and helper functions that streamline my Laravel and Homestead development workflow.",[1323,1324,1325,1314,1306],"bash","bashrc","git bash",{"path":28,"title":27,"description":1327,"tags":1328,"category":27,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Shell scripting and a faster life on the command line.",[],{"path":44,"title":43,"description":1330,"tags":1331,"category":37,"featured":301,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Practical rules for naming variables, functions and types so code reads like prose.",[1285,1332,1333,1334],"naming","readability","craft",{"path":38,"title":37,"description":1336,"tags":1337,"category":37,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Writing code that other people (and future you) can read.",[],{"path":52,"title":51,"description":1339,"tags":1340,"category":51,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Getting reliable, useful results out of language models.",[],{"path":86,"title":85,"description":1342,"tags":1343,"category":79,"featured":301,"updatedAt":1310,"createdAt":1310,"readingTime":298,"draft":1233},"The Git commands I actually use day to day, plus how to undo things safely.",[1267,1278,1344,1345],"version-control","cheat-sheet",{"path":80,"title":79,"description":1347,"tags":1348,"category":79,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Version control — everyday commands and getting out of trouble.",[],{"path":6,"title":10,"description":1350,"tags":1351,"category":10,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Languages, frameworks and the craft of building software.",[],{"path":109,"title":108,"description":1353,"tags":1354,"category":89,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":321,"draft":1233},"An introduction to AI-assisted software development, modern development workflows, and the mindset needed to use AI responsibly throughout the course.",[1261,1355,1356,1357],"software development","prompt engineering","beginners",{"path":113,"title":112,"description":1359,"tags":1360,"category":89,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":321,"draft":1233},"Learn how to write effective AI prompts and read JavaScript code confidently. This lesson teaches students how to direct AI instead of blindly copying its output.",[1261,1361,1356,1357],"javascript",{"path":117,"title":116,"description":1363,"tags":1364,"category":89,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":305,"draft":1233},"Introduction to the fundamentals of JavaScript, from variables and functions to loops, DOM manipulation, and events. This lesson prepares students for the Mini Product Store activity.",[1361,1357,1365,1366],"programming","web development",{"path":103,"title":102,"description":1368,"tags":1369,"category":102,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Session-by-session plans with objectives, timing and materials.",[],{"path":127,"title":126,"description":1371,"tags":1372,"category":120,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":305,"draft":1233},"Build a personal portfolio website using JavaScript arrays, loops, and DOM manipulation instead of hardcoded HTML.",[1257,1258,1361,1373,1374,1375],"portfolio","dom","loops",{"path":131,"title":130,"description":1377,"tags":1378,"category":120,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":305,"draft":1233},"A JavaScript activity where students build a simple product store using arrays, loops, functions, DOM manipulation, and event listeners.",[1257,1258,1361,1374,1379,1375],"arrays",{"path":121,"title":120,"description":1381,"tags":1382,"category":120,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"In-class activities that get students doing, not just listening.",[],{"path":145,"title":144,"description":1384,"tags":1385,"category":120,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":298,"draft":1233},"Practice reading JavaScript code, predicting its output, and verifying your understanding before running the program.",[1257,1258,1361,1291,1386,1387],"if-statement","debugging",{"path":139,"title":138,"description":1389,"tags":1390,"category":138,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Practice problems that build skill through repetition.",[],{"path":155,"title":154,"description":1392,"tags":1393,"category":148,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":321,"draft":1233},"A structured workflow for working with AI that emphasizes planning, review, and understanding before implementation.",[1261,1356,1280],{"path":149,"title":148,"description":1395,"tags":1396,"category":148,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Prompt templates for teaching with and about AI.",[],{"path":97,"title":96,"description":1398,"tags":1399,"category":89,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Lesson plans, activities, rubrics and student references.",[],{"path":90,"title":89,"description":1398,"tags":1401,"category":89,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},[],{"path":180,"title":179,"description":1403,"tags":1404,"category":172,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Two tiny functions to control how often a handler runs — with the difference explained.",[1361,1405,1406,1407],"performance","snippets","events",{"path":173,"title":172,"description":1409,"tags":1410,"category":172,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Small, copy-ready fragments you reach for again and again.",[],{"path":190,"title":189,"description":1412,"tags":1413,"category":162,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"The essential Vim motions and commands to get productive and get out.",[1414,1415,1345,1278],"vim","editor",{"path":183,"title":162,"description":1417,"tags":1418,"category":162,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Dense, scannable references for commands and syntax.",[],{"path":201,"title":200,"description":1420,"tags":1421,"category":193,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"A reusable PR description template that makes reviews faster and kinder.",[1267,1260,1422,1268],"template",{"path":194,"title":193,"description":1424,"tags":1425,"category":193,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Reusable starting points for files, configs and docs.",[],{"path":212,"title":211,"description":1427,"tags":1428,"category":204,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":298,"draft":1233},"The frontmatter and markdown conventions every note in this knowledge base follows.",[1429,1430,1431,1432],"meta","conventions","markdown","authoring",{"path":216,"title":215,"description":1434,"tags":1435,"category":204,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":298,"draft":1233},"Every formatting feature this knowledge base supports, on one page — a living reference and rendering test.",[1431,1436,1437],"mdc","reference",{"path":205,"title":204,"description":1439,"tags":1440,"category":204,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"How this knowledge base itself is built and maintained.",[],{"path":227,"title":226,"description":1442,"tags":1443,"category":219,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"What a CORS error actually means and how to fix it — on the server, where it belongs.",[1444,1445,1446,1387],"cors","http","apis",{"path":220,"title":219,"description":1448,"tags":1449,"category":219,"featured":1233,"updatedAt":1310,"createdAt":1310,"readingTime":279,"draft":1233},"Symptoms, causes and fixes for problems worth remembering.",[],1785598841175]