如何對SAP數據庫表進行增刪改查操作(2)?

本篇接着上一篇博文,繼續講解如何在 SAP 系統外部,方便地對 SAP 的數據庫表進行增刪改查操作。推薦的方式:

  • SAP 暴露 OData 服務供外部調用
  • SAP 暴露 Restful Service 供外部調用

總的來說,OData 是比較新的 Restful Service 規範,在 SAP 端編寫代碼相對容易,但早期版本不支持 OData; Restful Service 基本上較早的版本也可以實現。關於 OData 對 Netweaver 版本要求,請參考我另外一篇博文:

SAPUI5 (34) - OData Model 連接後端 SAP 系統 (上)

對於異構系統的接口,我比較喜歡服務化這個表述,Restful Service 能夠很好地體現服務化思想。站在第三方系統的角度,不管是 push 還是 pull 都能實現。

本文介紹基於 .net 平臺的 WinForm 框架如何實現 SAP 表的維護界面。在前端技術日新月異的今天,WinForm 少有人用,但 WinForm 作爲 Microsoft 早期的平臺技術,技術成熟,使用簡單,而且 Office 的 開發技術 VSTO ,允許使用 WinForm 框架,所以可以在 Office 中編寫界面,這也是極爲有用的一個應用場景。

RestSharp

我選擇開源的 RestSharp 框架作爲調用 Restful Service 的技術,RestSharp 使用起來靈活、強大。爲了減少後續代碼量,我對 HTTP 的 GET / POST / PUT / DELETE 請求進行封裝,認證方式選擇 Http Basic Authentication:

using RestSharp;
using RestSharp.Authenticators;
using System;

namespace RestSharpCRUD
{
    public class RestSharpHelper
    {
        private String username;
        private String password;

        public String BaseUrl { get; set; }

        private RestClient client;

        public RestSharpHelper(String baseUrl, String username, String password)
        {
            this.BaseUrl = baseUrl;
            this.username = username;
            this.password = password;

            // construct RestClient object
            client = new RestClient(this.BaseUrl)
            {
                Authenticator = new HttpBasicAuthenticator(username, password)
            };
        }

        public IRestResponse Get(String resource)
        {
            var req = new RestRequest(resource, Method.GET);
            var resp = client.Execute(req);

            return resp;
        }

        public IRestResponse Post(String resource, String payload)
        {
            var req = new RestRequest(resource, Method.POST);
            req.RequestFormat = DataFormat.Json;
            req.AddJsonBody(payload);
            req.AddParameter("application/json", payload, ParameterType.RequestBody);

            var resp = client.Execute(req);

            return resp;
        }

        public IRestResponse Put(String resource, String payload)
        {
            var req = new RestRequest(resource, Method.PUT);
            req.RequestFormat = DataFormat.Json;
            req.AddJsonBody(payload);
            req.AddParameter("application/json", payload, ParameterType.RequestBody);

            var resp = client.Execute(req);

            return resp;
        }

        public IRestResponse Delete(String resource)
        {
            var req = new RestRequest(resource, Method.DELETE);
            var resp = client.Execute(req);

            return resp;
        }
    }
}

Model 類

Model 類比較簡單,代碼如下:

using System;

namespace RestSharpCRUD {
    public class EmpEntity {
        public EmpEntity() {
            MANDT = "001";
        }

        public String MANDT { get; set; }
        public String EMPID { get; set; }
        public String EMPNAME { get; set; }
        public String EMPADDR { get; set; }
    }
}

調用 Restful Service

封裝了 Http 方法後,接下來編寫一個類,實現通過調用 Restful Service 來對 SAP zemployee 表增刪改查操作:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace RestSharpCRUD {
    public class EmpService {
        private String baseUrl = "http://sapecc6:8000";
        private String username = "stone";
        private String password = "123456";

        private RestSharpHelper restSharpHelper;

        public EmpService() {
            restSharpHelper = new RestSharpHelper(baseUrl, username, password);
        }

        public IList<EmpEntity> ListAll() {
            IList<EmpEntity> employees = null;

            var resp = restSharpHelper.Get("/zrest/employees");
            if (!resp.IsSuccessful) {                
                throw new Exception(resp.ErrorMessage);                
            }

            employees = JsonConvert.DeserializeObject<List<EmpEntity>>(resp.Content);
                       
            return employees;  
        }

        public bool Create(EmpEntity emp) {
            String payload = JsonConvert.SerializeObject(emp);

            // Call POST method
            var resp = restSharpHelper.Post("/zrest/employees/create", payload);

            if (!resp.IsSuccessful) {
                throw new Exception(resp.Content);
            }

            return true;
        }

        public bool Update(EmpEntity emp) {
            String payload = JsonConvert.SerializeObject(emp);

            // Call POST method
            var resource = String.Format("/zrest/employees/{0}", emp.EMPID);
            var resp = restSharpHelper.Put(resource, payload);

            if (!resp.IsSuccessful) {
                throw new Exception(resp.Content);
            }

            return true;
        }

        public bool Delete(String empId) {
            bool rv = false;

            var resource = String.Format("/zrest/employees/{0}", empId);
            var resp = restSharpHelper.Delete(resource);
            if (resp.IsSuccessful) rv = true;

            return rv;
        }        
    }
}

界面實現

我通過兩個 Form 的配合來實現增刪改查操作。第一個 Form 羅列所有的 employee,允許在 Form 中進行導航,雙擊跳轉到維護的 Form,可以在這個 Form 中進行刪除操作。設計時的界面如下:

代碼如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

namespace RestSharpCRUD
{
    public partial class EmpListForm : Form
    {
        public EmpListForm()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 加載數據
            IList<EmpEntity> employees = null;
            try {
                employees = LoadEmployees();
            }
            catch (Exception ex) {
                MessageBox.Show(ex.Message);
                return;
            }

            // 數據綁定到控件
            // List綁定到DataGridView不能進行增刪改查,所以將List轉換爲BindingList
            // DataGridView.DataSource = new BindingList<T>(List<T>);  
            bindingSource1.DataSource = new BindingList<EmpEntity>(employees);
            dataGridView1.DataSource = bindingSource1;
            bindingNavigator1.BindingSource = bindingSource1;
        }

        private void DataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            // 雙擊打開EmpSingleForm
            var empSingleForm = new EmpSingleForm(bindingSource1);
            empSingleForm.ShowDialog();
        }

        private void BindingNavigatorAddNewItem_Click(object sender, EventArgs e)
        {
            this.bindingSource1.AddNew();
            var empSingleForm = new EmpSingleForm(bindingSource1, true);
            empSingleForm.ShowDialog();
        }

        private void BindingNavigatorDeleteItem_Click(object sender, EventArgs e)
        {
            DoDelete();
        }

        private IList<EmpEntity> LoadEmployees()
        {
            var empService = new EmpService();
            IList<EmpEntity> employees = empService.ListAll();
            return employees;
        }

        private void DoDelete()
        {
            if (bindingSource1.Current == null) return;

            if (MessageBox.Show("確定刪除這個員工嗎?", "刪除",
                                MessageBoxButtons.YesNo,
                                MessageBoxIcon.Question) == DialogResult.Yes) {
                var empId = (bindingSource1.Current as EmpEntity).EMPID;

                var empService = new EmpService();
                bool rv = empService.Delete(empId);
                if (rv) {
                    bindingSource1.RemoveCurrent(); // 保持界面同步
                }
            }
        }
    }
}

UI 層與 Service 層交互,使用的是 IList<T> 格式,使用這種格式,在界面中要將 IList<T>,轉換爲 BindingList<T>,否則不支持 CRUD 操作。

在 EmpSingleForm 表單中,對單筆記錄進行更新和保存,表單設計時界面如下:

兩個表單都基於 BindingSource 控件對數據進行綁定,並且通過 BindingSource 控件在 Form 中交換數據,從而減少代碼量,爲此,在 EmpSingleForm 中,特意實現另外兩個構造函數:

#region constructors

public EmpSingleForm()
{
    InitializeComponent();
}

public EmpSingleForm(BindingSource bs) : this()
{
    empBs = bs;

    //設置數據綁定
    SetBinding();
}

public EmpSingleForm(BindingSource bs, bool addNew) : this(bs)
{
    isAddNewMode = addNew;
}

#endregion 

SetBinding() 方法負責數據的綁定:

private void SetBinding()
{
    txtEmpID.DataBindings.Add("Text", empBs, "EMPID", true);
    txtName.DataBindings.Add("Text", empBs, "EMPNAME", true);
    txtAddress.DataBindings.Add("Text", empBs, "EMPADDR", true);
}

將更新的數據保存到 SAP 後端,無非就是調用 EmpService 類的方法:

private void BtnSave_Click(object sender, System.EventArgs e)
{
    bool rv = false; // return value

    var emp = new EmpEntity
    {
        MANDT = "001",
        EMPID = txtEmpID.Text.Trim(),
        EMPNAME = txtName.Text.Trim(),
        EMPADDR = txtAddress.Text.Trim()
    };

    var empService = new EmpService();
    
    if (isAddNewMode) {
        try {
            rv = empService.Create(emp);
        }
        catch (Exception ex) {
            MessageBox.Show(ex.Message);
        }
    }
    else {
        rv = empService.Update(emp);
    }

    if (rv) {
        empBs.EndEdit();
        this.Close();
    }
}

以下是 EmpSingleForm 的完整代碼:

using System;
using System.Windows.Forms;

namespace RestSharpCRUD
{
    public partial class EmpSingleForm : Form
    {
        private bool isAddNewMode = false;

        #region constructors

        public EmpSingleForm()
        {
            InitializeComponent();
        }

        public EmpSingleForm(BindingSource bs) : this()
        {
            empBs = bs;

            //設置數據綁定
            SetBinding();
        }

        public EmpSingleForm(BindingSource bs, bool addNew) : this(bs)
        {
            isAddNewMode = addNew;
        }

        #endregion 

        private void EmpSingleForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.empBs.CancelEdit();
        }

        private void BtnSave_Click(object sender, System.EventArgs e)
        {
            bool rv = false; // return value

            var emp = new EmpEntity
            {
                MANDT = "001",
                EMPID = txtEmpID.Text.Trim(),
                EMPNAME = txtName.Text.Trim(),
                EMPADDR = txtAddress.Text.Trim()
            };

            var empService = new EmpService();
            
            if (isAddNewMode) {
                try {
                    rv = empService.Create(emp);
                }
                catch (Exception ex) {
                    MessageBox.Show(ex.Message);
                }
            }
            else {
                rv = empService.Update(emp);
            }

            if (rv) {
                empBs.EndEdit();
                this.Close();
            }
        }

        private void EmpSingleForm_Load(object sender, EventArgs e)
        {
            txtEmpID.Enabled = (isAddNewMode == true);
        }

        private void SetBinding()
        {
            txtEmpID.DataBindings.Add("Text", empBs, "EMPID", true);
            txtName.DataBindings.Add("Text", empBs, "EMPNAME", true);
            txtAddress.DataBindings.Add("Text", empBs, "EMPADDR", true);
        }
    }
}

程序源碼

該工程完整的代碼在 github

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章