├── host.json ├── test.http ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── local.settings.json.sample ├── Startup.cs ├── README.md ├── Models.cs ├── functions-csharp-entityframework.csproj ├── HttpTrigger.cs └── .gitignore /host.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0" 3 | } -------------------------------------------------------------------------------- /test.http: -------------------------------------------------------------------------------- 1 | GET http://localhost:7071/api/posts -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-azuretools.vscode-azurefunctions", 4 | "ms-vscode.csharp" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /local.settings.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "IsEncrypted": false, 3 | "Values": { 4 | "AzureWebJobsStorage": "", 5 | "FUNCTIONS_WORKER_RUNTIME": "dotnet", 6 | "SqlConnectionString": "" 7 | } 8 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Attach to .NET Functions", 6 | "type": "coreclr", 7 | "request": "attach", 8 | "processId": "${command:azureFunctions.pickProcess}" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "azureFunctions.deploySubpath": "bin/Release/netcoreapp3.0/publish", 3 | "azureFunctions.projectLanguage": "C#", 4 | "azureFunctions.projectRuntime": "~3", 5 | "debug.internalConsoleOptions": "neverOpen", 6 | "azureFunctions.preDeployTask": "publish" 7 | } 8 | -------------------------------------------------------------------------------- /Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Azure.Functions.Extensions.DependencyInjection; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using System; 5 | 6 | [assembly: FunctionsStartup(typeof(functions_csharp_entityframeworkcore.Startup))] 7 | 8 | namespace functions_csharp_entityframeworkcore 9 | { 10 | class Startup : FunctionsStartup 11 | { 12 | public override void Configure(IFunctionsHostBuilder builder) 13 | { 14 | string SqlConnection = Environment.GetEnvironmentVariable("SqlConnectionString"); 15 | builder.Services.AddDbContext( 16 | options => options.UseSqlServer(SqlConnection)); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # .NET Core Entity Framework and functions 2 | 3 | C# Function that uses dependency injection and entity framework core with Azure SQL. A few things of note: 4 | 5 | I want to use design-first migration, so I created a `IDesignTimeDbContextFactory` for the tooling to generate the right `DbContext`. 6 | 7 | I also had to add a post-build step to the `.csproj` file to make sure the `.dll` for the project is where the entity framework tooling expected: 8 | 9 | Windows 10 | ```xml 11 | 12 | 13 | 14 | ``` 15 | 16 | Mac 17 | ```xml 18 | 19 | 20 | 21 | ``` -------------------------------------------------------------------------------- /Models.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace functions_csharp_entityframeworkcore 8 | { 9 | public class BloggingContext : DbContext 10 | { 11 | public BloggingContext(DbContextOptions options) 12 | : base(options) 13 | { } 14 | 15 | public DbSet Blogs { get; set; } 16 | public DbSet Posts { get; set; } 17 | } 18 | 19 | public class Blog 20 | { 21 | public int BlogId { get; set; } 22 | public string Url { get; set; } 23 | 24 | public ICollection Posts { get; set; } 25 | } 26 | 27 | public class Post 28 | { 29 | public int PostId { get; set; } 30 | public string Title { get; set; } 31 | public string Content { get; set; } 32 | 33 | public int BlogId { get; set; } 34 | public Blog Blog { get; set; } 35 | } 36 | 37 | public class BloggingContextFactory : IDesignTimeDbContextFactory 38 | { 39 | public BloggingContext CreateDbContext(string[] args) 40 | { 41 | var optionsBuilder = new DbContextOptionsBuilder(); 42 | optionsBuilder.UseSqlServer(Environment.GetEnvironmentVariable("SqlConnectionString")); 43 | 44 | return new BloggingContext(optionsBuilder.Options); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /functions-csharp-entityframework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netcoreapp3.1 4 | v3 5 | functions_csharp_entityframework 6 | 7 | 8 | 9 | 10 | runtime; build; native; contentfiles; analyzers; buildtransitive 11 | all 12 | 13 | 14 | 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | all 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | PreserveNewest 26 | Never 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "clean", 6 | "command": "dotnet", 7 | "args": [ 8 | "clean", 9 | "/property:GenerateFullPaths=true", 10 | "/consoleloggerparameters:NoSummary" 11 | ], 12 | "type": "process", 13 | "problemMatcher": "$msCompile" 14 | }, 15 | { 16 | "label": "build", 17 | "command": "dotnet", 18 | "args": [ 19 | "build", 20 | "/property:GenerateFullPaths=true", 21 | "/consoleloggerparameters:NoSummary" 22 | ], 23 | "type": "process", 24 | "dependsOn": "clean", 25 | "group": { 26 | "kind": "build", 27 | "isDefault": true 28 | }, 29 | "problemMatcher": "$msCompile" 30 | }, 31 | { 32 | "label": "clean release", 33 | "command": "dotnet", 34 | "args": [ 35 | "clean", 36 | "--configuration", 37 | "Release", 38 | "/property:GenerateFullPaths=true", 39 | "/consoleloggerparameters:NoSummary" 40 | ], 41 | "type": "process", 42 | "problemMatcher": "$msCompile" 43 | }, 44 | { 45 | "label": "publish", 46 | "command": "dotnet", 47 | "args": [ 48 | "publish", 49 | "--configuration", 50 | "Release", 51 | "/property:GenerateFullPaths=true", 52 | "/consoleloggerparameters:NoSummary" 53 | ], 54 | "type": "process", 55 | "dependsOn": "clean release", 56 | "problemMatcher": "$msCompile" 57 | }, 58 | { 59 | "type": "func", 60 | "dependsOn": "build", 61 | "options": { 62 | "cwd": "${workspaceFolder}/bin/Debug/netcoreapp3.1" 63 | }, 64 | "command": "host start", 65 | "isBackground": true, 66 | "problemMatcher": "$func-watch" 67 | } 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /HttpTrigger.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.Azure.WebJobs; 4 | using Microsoft.Azure.WebJobs.Extensions.Http; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.Extensions.Logging; 7 | using System.Linq; 8 | using System; 9 | using System.Threading; 10 | using Newtonsoft.Json; 11 | using System.IO; 12 | 13 | namespace functions_csharp_entityframeworkcore 14 | { 15 | public class HttpTrigger 16 | { 17 | private readonly BloggingContext _context; 18 | public HttpTrigger(BloggingContext context) 19 | { 20 | _context = context; 21 | } 22 | 23 | [FunctionName("GetPosts")] 24 | public IActionResult GetPosts( 25 | [HttpTrigger(AuthorizationLevel.Function, "get", Route = "posts")] HttpRequest req, 26 | ILogger log) 27 | { 28 | log.LogInformation("C# HTTP GET/posts trigger function processed a request."); 29 | 30 | var postsArray = _context.Posts.OrderBy(p => p.Title).ToArray(); 31 | return new OkObjectResult(postsArray); 32 | } 33 | 34 | [FunctionName("PostPost")] 35 | public async Task PostPostAsync( 36 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = "blog/{blogId}/post")] HttpRequest req, 37 | int blogId, 38 | CancellationToken cts, 39 | ILogger log) 40 | { 41 | log.LogInformation("C# HTTP POST/post trigger function processed a request."); 42 | 43 | string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); 44 | Post data = JsonConvert.DeserializeObject(requestBody); 45 | 46 | Post p = new Post 47 | { 48 | BlogId = blogId, 49 | Content = data.Content, 50 | Title = data.Title 51 | }; 52 | var entity = await _context.Posts.AddAsync(p, cts); 53 | await _context.SaveChangesAsync(cts); 54 | return new OkObjectResult(JsonConvert.SerializeObject(entity.Entity)); 55 | } 56 | 57 | [FunctionName("PostBlog")] 58 | public async Task PostBlogAsync( 59 | [HttpTrigger(AuthorizationLevel.Function, "post", Route = "blog")] HttpRequest req, 60 | CancellationToken cts, 61 | ILogger log) 62 | { 63 | log.LogInformation("C# HTTP POST/blog trigger function processed a request."); 64 | 65 | var entity = await _context.Blogs.AddAsync(new Blog(), cts); 66 | await _context.SaveChangesAsync(cts); 67 | return new OkObjectResult(JsonConvert.SerializeObject(entity.Entity)); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # Azure Functions localsettings file 5 | local.settings.json 6 | Migrations/ 7 | .env 8 | 9 | # User-specific files 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Build results 19 | [Dd]ebug/ 20 | [Dd]ebugPublic/ 21 | [Rr]elease/ 22 | [Rr]eleases/ 23 | x64/ 24 | x86/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # DNX 49 | project.lock.json 50 | project.fragment.lock.json 51 | artifacts/ 52 | 53 | *_i.c 54 | *_p.c 55 | *_i.h 56 | *.ilk 57 | *.meta 58 | *.obj 59 | *.pch 60 | *.pdb 61 | *.pgc 62 | *.pgd 63 | *.rsp 64 | *.sbr 65 | *.tlb 66 | *.tli 67 | *.tlh 68 | *.tmp 69 | *.tmp_proj 70 | *.log 71 | *.vspscc 72 | *.vssscc 73 | .builds 74 | *.pidb 75 | *.svclog 76 | *.scc 77 | 78 | # Chutzpah Test files 79 | _Chutzpah* 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opendb 86 | *.opensdf 87 | *.sdf 88 | *.cachefile 89 | *.VC.db 90 | *.VC.VC.opendb 91 | 92 | # Visual Studio profiler 93 | *.psess 94 | *.vsp 95 | *.vspx 96 | *.sap 97 | 98 | # TFS 2012 Local Workspace 99 | $tf/ 100 | 101 | # Guidance Automation Toolkit 102 | *.gpState 103 | 104 | # ReSharper is a .NET coding add-in 105 | _ReSharper*/ 106 | *.[Rr]e[Ss]harper 107 | *.DotSettings.user 108 | 109 | # JustCode is a .NET coding add-in 110 | .JustCode 111 | 112 | # TeamCity is a build add-in 113 | _TeamCity* 114 | 115 | # DotCover is a Code Coverage Tool 116 | *.dotCover 117 | 118 | # NCrunch 119 | _NCrunch_* 120 | .*crunch*.local.xml 121 | nCrunchTemp_* 122 | 123 | # MightyMoose 124 | *.mm.* 125 | AutoTest.Net/ 126 | 127 | # Web workbench (sass) 128 | .sass-cache/ 129 | 130 | # Installshield output folder 131 | [Ee]xpress/ 132 | 133 | # DocProject is a documentation generator add-in 134 | DocProject/buildhelp/ 135 | DocProject/Help/*.HxT 136 | DocProject/Help/*.HxC 137 | DocProject/Help/*.hhc 138 | DocProject/Help/*.hhk 139 | DocProject/Help/*.hhp 140 | DocProject/Help/Html2 141 | DocProject/Help/html 142 | 143 | # Click-Once directory 144 | publish/ 145 | 146 | # Publish Web Output 147 | *.[Pp]ublish.xml 148 | *.azurePubxml 149 | # TODO: Comment the next line if you want to checkin your web deploy settings 150 | # but database connection strings (with potential passwords) will be unencrypted 151 | #*.pubxml 152 | *.publishproj 153 | 154 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 155 | # checkin your Azure Web App publish settings, but sensitive information contained 156 | # in these scripts will be unencrypted 157 | PublishScripts/ 158 | 159 | # NuGet Packages 160 | *.nupkg 161 | # The packages folder can be ignored because of Package Restore 162 | **/packages/* 163 | # except build/, which is used as an MSBuild target. 164 | !**/packages/build/ 165 | # Uncomment if necessary however generally it will be regenerated when needed 166 | #!**/packages/repositories.config 167 | # NuGet v3's project.json files produces more ignoreable files 168 | *.nuget.props 169 | *.nuget.targets 170 | 171 | # Microsoft Azure Build Output 172 | csx/ 173 | *.build.csdef 174 | 175 | # Microsoft Azure Emulator 176 | ecf/ 177 | rcf/ 178 | 179 | # Windows Store app package directories and files 180 | AppPackages/ 181 | BundleArtifacts/ 182 | Package.StoreAssociation.xml 183 | _pkginfo.txt 184 | 185 | # Visual Studio cache files 186 | # files ending in .cache can be ignored 187 | *.[Cc]ache 188 | # but keep track of directories ending in .cache 189 | !*.[Cc]ache/ 190 | 191 | # Others 192 | ClientBin/ 193 | ~$* 194 | *~ 195 | *.dbmdl 196 | *.dbproj.schemaview 197 | *.jfm 198 | *.pfx 199 | *.publishsettings 200 | node_modules/ 201 | orleans.codegen.cs 202 | 203 | # Since there are multiple workflows, uncomment next line to ignore bower_components 204 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 205 | #bower_components/ 206 | 207 | # RIA/Silverlight projects 208 | Generated_Code/ 209 | 210 | # Backup & report files from converting an old project file 211 | # to a newer Visual Studio version. Backup files are not needed, 212 | # because we have git ;-) 213 | _UpgradeReport_Files/ 214 | Backup*/ 215 | UpgradeLog*.XML 216 | UpgradeLog*.htm 217 | 218 | # SQL Server files 219 | *.mdf 220 | *.ldf 221 | 222 | # Business Intelligence projects 223 | *.rdl.data 224 | *.bim.layout 225 | *.bim_*.settings 226 | 227 | # Microsoft Fakes 228 | FakesAssemblies/ 229 | 230 | # GhostDoc plugin setting file 231 | *.GhostDoc.xml 232 | 233 | # Node.js Tools for Visual Studio 234 | .ntvs_analysis.dat 235 | 236 | # Visual Studio 6 build log 237 | *.plg 238 | 239 | # Visual Studio 6 workspace options file 240 | *.opt 241 | 242 | # Visual Studio LightSwitch build output 243 | **/*.HTMLClient/GeneratedArtifacts 244 | **/*.DesktopClient/GeneratedArtifacts 245 | **/*.DesktopClient/ModelManifest.xml 246 | **/*.Server/GeneratedArtifacts 247 | **/*.Server/ModelManifest.xml 248 | _Pvt_Extensions 249 | 250 | # Paket dependency manager 251 | .paket/paket.exe 252 | paket-files/ 253 | 254 | # FAKE - F# Make 255 | .fake/ 256 | 257 | # JetBrains Rider 258 | .idea/ 259 | *.sln.iml 260 | 261 | # CodeRush 262 | .cr/ 263 | 264 | # Python Tools for Visual Studio (PTVS) 265 | __pycache__/ 266 | *.pyc --------------------------------------------------------------------------------