using System; using System.IO; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json;
using (var db = new DemoContext()) { Console.WriteLine("===== Departments =========="); foreach (var dept in db.Dept) { Console.WriteLine($"{dept.Deptno}{dept.Dname}{dept.Loc}"); }
using (var db = new DemoContext()) { var employees = db.Emp .Where(e => e.Deptno == 10 || e.Deptno == 30) .OrderBy(e => e.Deptno) .ThenBy(e => e.Empno);
foreach (var e in employees) { Console.WriteLine($"{e.Empno}{e.Ename}{e.Job}{e.Mgr}{e.Hiredate?.ToString("yyyy-MM-ddTHH:mm:ss")}{e.Sal}{e.Comm}{e.Deptno}"); } }
現在將 Master-Detail 資料關聯起來,DEPT 對 EMP 是一對多的關係。
Program.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
using (var db = new DemoContext()) { var employees = db.Emp.ToList();
var department = db.Dept.Where(d => d.Deptno == 20).FirstOrDefault(); Console.WriteLine($"=== {department.Deptno}{department.Dname}{department.Loc} ===");
foreach (var e in department.Emp) { Console.WriteLine($"{e.Empno}{e.Ename}{e.Job}{e.Mgr}{e.Hiredate?.ToString("yyyy-MM-ddTHH:mm:ss")}{e.Sal}{e.Comm}{e.Deptno}"); } }
$ OracleEFCoreSample> dotnet run Hello Tainan! Hi 台南 === 20 RESEARCH DALLAS === 7566 陳賜珉 MANAGER 7839 1981-04-02T00:00:00 2975 20 7788 SCOTT ANALYST 7566 1982-12-09T00:00:00 45300 20 7902 FORD ANALYST 7566 1981-12-03T00:00:00 3000 20 7369 SMITH CLERK 7902 1980-12-17T00:00:00 8001 20 7876 ADAMS CLERK 7788 1983-01-12T00:00:00 1100 20
using (var db = new DemoContext()) { db.Emp.AddRange( new Emp { Empno = 9681, Ename = "C#範例2", Job = "ANALYST", Mgr = 7566, Hiredate = DateTime.Now, Sal = 13000, Comm = 130, Deptno = 10 }, new Emp { Empno = 9682, Ename = "C#範例3", Job = "ANALYST", Mgr = 7566, Hiredate = DateTime.Now, Sal = 33000, Comm = 330, Deptno = 20 } ); int records = db.SaveChanges(); Console.WriteLine($"{records} records saved to database."); }
更新紀錄
Program.cs
1 2 3 4 5 6 7 8 9 10 11
using (var db = new DemoContext()) { int records = 0; var employee = db.Emp.Where(e => e.Empno == 9588).FirstOrDefault(); if (employee != null) { employee.Comm = 500; records = db.SaveChanges(); } Console.WriteLine($"{records} records updated."); }
刪除紀錄
Program.cs
1 2 3 4 5 6 7 8 9 10 11
using (var db = new DemoContext()) { int records = 0; var employee = db.Emp.Where(e => e.Empno == 9588).FirstOrDefault(); if (employee != null) { db.Emp.Remove(employee); records = db.SaveChanges(); } Console.WriteLine($"{records} records deleted from database."); }
可以用 RemoveRange 刪除多筆資料。
Program.cs
1 2 3 4 5 6 7
using (var db = new DemoContext()) { var employees = db.Emp.Where(e => e.Empno == 9681 || e.Empno == 9682); db.Emp.RemoveRange(employees); int records = db.SaveChanges(); Console.WriteLine($"{records} records deleted from database."); }
物件關係映射工具,如 EF Core,並不適用於所有場景。例如一次刪除很多資料或所有的資料就沒有那麼高效能,使用單個 SQL 語句可以刪除所有資料,會比每一個紀錄使用一個 DELETE 語句效能高得多。
這是一個 Windows 作業系統下的 Desktop 應用程式,可以實際上線應用的即時訊息通知應用。這是在 Windows 10 下開發的,用到的軟體版本比較新,Windows 10 以下沒有測試過,也沒有壓力測試。這是 Windows 作業系統的 Desktop 應用程式,無法用在行動裝置下運作。
應用程式啟動後,會釘選在 Windows 右下角的托盤(Tray)上。
我的 Windows 還沒有合法版權,有個浮水印,有點模糊。當有通知 (Notification) 時,Windows 的右下角會馬上顯示通知。
return l_message; exception when no_data_found then return '"{}"'; end; -- main body begin for rec in (selectrownum, empno from emp where empno = 7788) loop l_item := message(rec.empno); l_messages := l_messages||case rec.rownum when 1 then '' else ',' end||l_item; endloop;
-- process the response from the HTTP call begin loop utl_http.read_line(l_http_response, l_buffer); dbms_output.put_line(l_buffer); endloop; utl_http.end_response(l_http_response); exception when utl_http.end_of_body then utl_http.end_response(l_http_response); end; end; /
return l_message; exception when no_data_found then return '"{}"'; end; -- main body begin for rec in (selectrownum, empno from emp where empno = 7788) loop l_item := message(rec.empno); l_messages := l_messages||case rec.rownum when 1 then '' else ',' end||l_item; endloop;
-- process the response from the HTTP call begin loop utl_http.read_line(l_http_response, l_buffer); dbms_output.put_line(l_buffer); endloop; utl_http.end_response(l_http_response); exception when utl_http.end_of_body then utl_http.end_response(l_http_response); end; end; /
package.json 的第 56,57 行則是 Electron 的習慣性基本的命名程式檔,簡單的看一下 Electron 的基本架構:
The main process
Main process 有幾個重要職責。它可以回應應用程序生命週期事件,例如啟動,退出,準備退出,進入後台,進入前台等等,Main process 還負責與本機操作系統 API 進行通信。 如果要顯示一個對話框以打開或保存文件,就得從 Main process 進行操作。
Renderer processes
Main process 可以使用 Electron 的 Browser-Window 模塊創建和銷毀 Renderer process。 Renderer process 可以加載網頁以顯示客戶端的 GUI。 每個進程都利用 Chronium 的多進程架構,並在其自己的線程(Thread)上運行。 然後,這些頁面可以加載其他 JavaScript 文件並在此線程中執行代碼。 與普通網頁不同,您可以從 Renderer process 訪問所有 Node API,從而可以使用本機模塊和較低級別的系統功能溝通。
Writing C:\Users\76070049\AppData\Local\Temp\tmpBD4F.tmp info : 正在將套件 'Oracle.ManagedDataAccess.Core' 的 PackageReference 新增至專案 'I:\C#\TrainSample\MyWeb\MyWeb.csproj'。 info : 正在還原 I:\C#\TrainSample\MyWeb\MyWeb.csproj 的封裝... info : GET https://api.nuget.org/v3-flatcontainer/oracle.manageddataaccess.core/index.json info : OK https://api.nuget.org/v3-flatcontainer/oracle.manageddataaccess.core/index.json 710 毫秒 info : GET https://api.nuget.org/v3-flatcontainer/oracle.manageddataaccess.core/2.19.60/oracle.manageddataaccess.core.2.19.60.nupkge.2.19.60.nupkg .2.19.60.nupkg 720 毫秒 info : OK https://api.nuget.org/v3-flatcontainer/oracle.manageddataaccess.core/2.19.60/oracle.manageddataaccess.core.2.19.60.nupkg 720 毫秒 info : 正在安裝 Oracle.ManagedDataAccess.Core 2.19.60。 \MyWeb.csproj'。 info : 套件 'Oracle.ManagedDataAccess.Core' 與專案 'I:\C#\TrainSample\MyWeb\MyWeb.csproj' 中的所有架構相容。 info : 已將套件 'Oracle.ManagedDataAccess.Core' 版本 '2.19.60' 的 PackageReference 新增至檔案 'I:\C#\TrainSample\MyWeb\MyWeb.csproj'。 info : 正在認可還原... info : 正在將資產檔案寫入磁碟。路徑: I:\C#\TrainSample\MyWeb\obj\project.assets.json log : I:\C#\TrainSample\MyWeb\MyWeb.csproj 的還原於 3.98 sec 完成。
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging;
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting;
namespaceMyWeb { publicclassStartup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 publicvoidConfigureServices(IServiceCollection services) { }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. publicvoidConfigure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using IHostApplicationLifetime = Microsoft.Extensions.Hosting.IHostApplicationLifetime;
namespaceMyWeb { publicclassStartup { publicStartup() { Program.MyOutput("Startup Contructor called."); } // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 publicvoidConfigureServices(IServiceCollection services) { Program.MyOutput("Startup.ConfigureServices called."); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. publicvoidConfigure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime appLifetime) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
function update(deptno, data) { const statement = ` UPDATE dept d SET d.dname = :dname, d.loc = :loc WHERE d.deptno = :deptno RETURNING ROWID INTO :rid `;
return new Promise(async (resolve, reject) => { if (!deptno) { reject(new Error("Invalid deptno")); } if (typeof data !== "object") { reject(new Error("Input must be an valid object")); }
let result; let binds = { deptno: setValue(deptno), dname: setValue(data["dname"]), loc: setValue(data["loc"]), rid: { type: oracledb.STRING, dir: oracledb.BIND_OUT } };
function upsert(data) { const statement = ` MERGE INTO dept d USING (select :deptno as deptno, :dname as dname, :loc as loc from dual) p ON (d.deptno = p.deptno) WHEN MATCHED THEN UPDATE SET d.dname = p.dname, d.loc = p.loc WHEN NOT MATCHED THEN INSERT (d.deptno, d.dname, d.loc) VALUES (p.deptno, p.dname, p.loc) `;
return new Promise(async (resolve, reject) => { if (typeof data !== "object") { reject(new Error("Input must be an array")); }
if (data instanceof Array && data.length > 0) { let result; let binds = [];
returnnewPromise(async (resolve, reject) => { if (!empno) { reject(newError("Invalid empno")); } if (typeof data !== "object") { reject(newError("Input must be an valid object")); }
functionupsert(data) { const statement = ` MERGE INTO emp e USING (select :empno as empno, :ename as ename, :job as job, :mgr as mgr, to_date(:hiredate,'yyyy-mm-dd"T"hh24:mi:ss') as hiredate, :sal as sal, :comm as comm, :deptno as deptno from dual) p ON (e.empno = p.empno) WHEN MATCHED THEN UPDATE SET e.ename = p.ename, e.job = p.job, e.mgr = p.mgr, e.hiredate = p.hiredate, e.sal = p.sal, e.comm = p.comm, e.deptno = p.deptno WHEN NOT MATCHED THEN INSERT (e.empno, e.ename, e.job, e.mgr, e.hiredate, e.sal, e.comm, e.deptno) VALUES (p.empno, p.ename, p.job, p.mgr, p.hiredate, p.sal, p.comm, p.deptno) `;
returnnewPromise(async (resolve, reject) => { if (typeof data !== "object") { reject(newError("Input must be an array")); }
if (data instanceofArray && data.length > 0) { let result; let binds = [];
module.exports = { Query: { allPhotosWithPagination: async (parent, { after, first, before, last }, { db: { photos } }) => { let result, allCount, unlimitedCount; const opts = {}; if (typeof after === 'string' && typeof before === 'string' ) { thrownewError('after and before cannot use same time'); } if (typeof first === 'number' && typeof last === 'number') { thrownewError('first and last cannot use same time'); } if (typeof after === 'string' && typeof last === 'number') { thrownewError('after must be with first'); } if (typeof before === 'string' && typeof first === 'number') { thrownewError('before must be with last'); } // Forward pagination if (typeof first === 'number') { if (first < 0 ) thrownewError('first cannot be less than 0'); opts.limit = first }; if (typeof after === 'string') opts.gt = fromCursorHash(after); // Backward pagination if (typeof last === 'number') { if (last < 0 ) thrownewError('last cannot be less than 0'); opts.limit = last; } if (typeof before === 'string') opts.lt = fromCursorHash(before); if (typeof last === 'number' || typeof before === 'string') opts.reverse = true; try { allCount = await photos.allCount(); unlimitedCount = await photos.allCount({ ...opts, limit: undefined }); result = await photos.findAll( opts ); } catch (error) { throw error; }
在 Apollo Client 底層,網路請求是用 ApolloLink 來管理的。在目前 app 中,它負責傳送 HTTP 請求給 GraphQL 伺服器。每當我們用 Apollo Client 傳送操作時,那個操作就會被送到一個 Apollo Link 來處理網路請求。我們也可以使用 Apollo Link 來處理 WebSocket 上的網路。