Reyza

tenang, senang, sederhana
See also: Other Geeks@INDC

Public

April 2011 - Posts

{Linq to SQL - Optimistic Concurrency} on ASP.NET

Misalnya seorang user, sebut saja berinisial MD sedang mengakses halaman berikut :

image imageimage

Begitu juga dengan user lain yang berinisial IR, mengkases halaman yang sama dan sedang mengedit record yang saja juga.

imageimageimage

Bila hal tersebut di atas terjadi, dan saat salah satu user melakukan proses Update, kira apa yang terjadi ?

  1. apakah data akan diupdate sesuai dengan user yang terakhir melakukan proses update?
  2. apakah user yang mengklik duluan yang akan berhasil mengupdate data, sedangkan user yang kemudian mengupdate akan melakukan kegagalan?
  3. ataukah, data akan di-merge antara data dari kedua user tersebut?

Kenapa perlu dipikirkan hal seperti di atas?

  1. Jika kasus 1 yang dianut, maka hanya user yang terakhir yang tahu akan kebenaran data yang user tersebut inputkan telah disimpan. Sedangkan user sebelumnya akan tenang-tenang saja dan merasa data yang telah dia inputkan telah masuk, padahal data tersebut sudah terubah oleh user terakhir. Secara logika mungkin benar, tetapi secara etika terhadap user pengguna mungkin kurang, karena user sebelum yang terakhir jadi korban…korban “tenang-tenang saja” padahal data dia telah berubah.
  2. Sedangkan kasus kedua atau ketiga mungkin akan lebih manusiawi.

Tapi pada posting ini hanya akan dibahas kasus kedua saja, yang lebih dikenal dengan Optimistic Concurrency.  Bila digunakan kebetulan digunakan teknologi Linq to SQL maka secara default akan digunakan metode Optimistic Concurrency, artinya jika satu record diakses bersamaan oleh MD dan IR maka :

  1. Jika user MD menyimpan/update terlebih dahulu maka user IR akan menemui pesan kegagalan.
  2. Jika user IR yang melakukan proses penyimpanan/update terlebih dahulu maka user MD akan menemui kegagalan saat menyimpan.

Berikut adalah contoh sederhana halaman yang berisi daftar data dan form inline editing pada GridView.

   1: <asp:LinqDataSource ID="LinqDataSource1" runat="server" 
   2:     ContextTypeName="DataClassesDataContext" EnableDelete="True" 
   3:     EnableInsert="True" EnableUpdate="True" EntityTypeName="" TableName="REF_AXLEs">
   4: </asp:LinqDataSource>
   5:     
   6: <br />
   7: <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
   8:     DataKeyNames="AXLE_CODE" DataSourceID="LinqDataSource1" AllowPaging="True">
   9:     <Columns>
  10:         <asp:CommandField ShowEditButton="True" />
  11:         <asp:BoundField DataField="AXLE_CODE" HeaderText="AXLE_CODE" ReadOnly="True" SortExpression="AXLE_CODE" />
  12:         <asp:BoundField DataField="DESCRIPTION" HeaderText="DESCRIPTION" SortExpression="DESCRIPTION" />
  13:     </Columns>
  14: </asp:GridView>

Cara menguji kebenaran bahwa Linq to SQL dapat dilakukan dengan skenario seperti berikut :

Cukup gunakan 1 browser dengan dua tab yang berbeda untuk mengakses 1 halaman.

image

Pada tab 1, klik Edit pada AXLE_CODE=4, begitu juga pada tab 2.

Kemudian pada Tab 1 ubah data pada kolom Description klik tombol Update, proses Update berhasil.

Pada Tab 2, ubah data pada kolom Description kemudian klik tombol Update, proses gagal.

Dan berikut adalah contoh pesan kegagalan yang akan didapatkan oleh user yang kemudian menyimpan/update setelah user pertama melakukan update. Dapat dilihat terdapat pesan kegagalan karena kolom telah berubah atau tidak ditemukan.

image

Sebenarnya apa sih yang terjadi ketika proses update jika menggunakan Linq to SQL (atau “sejenisnya”) ? Berikut kira yang terjadi jika diterangkan secara sederhana :

  1. Ketika tombol Edit diklik, ada yang berperan untuk “menampung” nilai original dari record yang dipilih.
  2. Saat proses penyimpanan/update nilai original tersebut akan dibandingkan dengan data yang ada di database, jika nilai original dari field yang kita ubah berbeda dengan nilai yang ada di database, maka artinya ada yang telah mengubah nilai tersebut, maka akan dilemparkan pesan kegagalan.

Kasus di atas menggunakan control LinqDataSource dan GridView dengan inline editing. Bagaimana bila kasusnya dilakukan semi-manual seperti kasus di bawah ini.

image

Ketika link Detail pada suatu record dipilih, maka sebenarnya yang dipanggil adalah event OnSelectedIndexChanged dari GridView, dan pada event handlernya dilakukan pengisian nilai pada control-control seperti TextBox, DropDownList berdasarkan record yang dipilih, seperti pada gambar di bawah ini. Untuk menyimpan nilai original record dapat memanfaatkan Session seperti berikut :

   1: string id = Convert.ToString(GridView_Main.SelectedValue);
   2: REF_AXLE item = repository.Get<REF_AXLE>(id);
   3: Session["REF_AXLE"] = item;

Baris ke-2 adalah cara untuk “menangkap object” dr record yang dipilih, dan baris ke-3 adalah cara untuk menyimpan objek tersebut ke dalam Session.

image

Jika tombol Update (ImageButton), maka akan dieksekusi event handler OnClick dari ImageButton tersebut. Pada event handler ini yang dilakuan adalah mengambil kembali nilai pada Session.

   1: REF_AXLE originalitem = (REF_AXLE)Session["REF_AXLE"];

Kemudian menampung nilai yang akan diupdate pada objek :

   1: REF_AXLE item = new REF_AXLE();
   2:             
   3: item.AXLE_CODE = TextBox_FormCode.Text;
   4: item.DESCRIPTION = TextBox_FormName.Text;

Selanjutkan tinggal attach seperti berikut :

   1: DataAccessObject db = new DataAccessObject();
   2: db.DataContext.REF_AXLEs.Attach(item, originalitem);
   3: db.DataContext.SubmitChanges();

Baris ke-1 adalah class buatan sendiri, bukan default ada. Intinya adalah pada baris ke-2, ada proses attach dengan menggunakan dua objek, yaitu objek original dan objek dengan nilai untuk update. Hal ini memastikan Optimistic Concurrency masih berjalan.

#DokumentasiAgarTidakLupa

Share this post: | | | |
Posted: Apr 06 2011, 09:20 PM by reyza | with no comments
Filed under: , ,
ASP.NET : Using RoleManager and Profile in different way

Sejak ASP.NET 2.0 telah dikenal fitur Membership, RoleManager dan Profile, karena fitur lama jadi tidak perlu di jelaskan secara detail, karena dipastikan sudah pada tahu kalau :

  • Membership berfungsi membantu untuk mengelola user.
  • RoleManager berfungsi membantu untuk role dari user.
  • Profile berfungsi untuk menambah atribut pada user, seperti alamat, organisasi kerja dan lain-lain.

Sedangkan untuk membuat menu telah disediakan Sitemap yang dapat terintegrasi dengan fitur di atas.  Menu pada Sitemap dapat juga dapat berkerja sama dengan RoleManager, sehingga memungkinkan penampakan item-item pada menu dapat diatur sesuai dengan role dari user yang sedang login.  Untuk membuat mereka bekerjasama pun sangat mudah, pada file *.sitemap cukup tambahkan atribut Roles pada suatu item menu, dan berikan nama role yang  dapat mengakses item menu tersebut.

User memungkinkan dapat memiliki lebih dari role, sehingga ….

  • Jika pada Sitemap diatur item menu A, B, C adalah untuk role User,
  • Sedangkan item menu D, E, F, G adalah untuk role Admin,

Maka ketika seorang user memiliki role User dan Admin, secara otomatis akan ditampilkan item menu A, B, C, D, E, F, G.

Tetapi untuk sebagian orang mungkin ingin memiliki cara yang berbeda dengan cara di atas, sebagian orang mungkin ingin menggunakan cara penanganan seperti berikut :

  • User dapat memiliki lebih dari satu role, misal role yang telah didaftarkan adalah A, B, C dan D. Maka seorang user dapat memiliki role {A dan B} dari {A, B, C dan D}.
  • Setelah user login, terlebih dahulu user tersebut dapat memilih role yang ingin digunakan, role yang dapat dipilih adalah role yang telah didaftarkan ke user tersebut saja, dalam kasus ini adalah role {A dan B} saja.
  • Item-item menu yang ditampilkan akan sesuai dengan role yang dipilih.

Untuk membuat permintaan tersebut terlaksana maka perlu dilakukan langkah-langkah di bawah ini.

{Memanfaatkan Profile sebagai media penyimpanan role dari user}

Normalnya, role yang dimiliki oleh user disimpan pada tabel aspnet_UsersInRoles, bila user mempunyai lebih dari 1 role, misalnya 3 role, maka akan ditemui 3 record pada tabel ini. Dengan informasi itulah menu dirender berdasarkan role dari user yang sedang login.  Karena yang diinginkan hanya 1 role saja yang aktif berdasarkan pilihan user tersebut setelah login, maka tabel ini hanya akan berisi 1 role saja, yaitu role yang dipilih oleh user tersebut. 

image

Tetapi karena user masih mungkin memiliki lebih dari maka perlu media penyimpanan lain, oleh karena itu dapat memanfaatkan Profile untuk kebutuhan ini. Untuk keperluan itu perlu ditambahkan 1 atribut untuk menyimpan data role-role tersebut.  Penambahan atribut ini cukup dilakukan pada file web.config, dan tambahkan kode seperti paga baris ke-7.

   1: <profile>
   2:     <providers>
   3:         <clear/>
   4:         <add name="AspNetSqlProfileProvider" connectionStringName="HOREHOREUserConnString" applicationName="HOREHORE" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
   5:     </providers>
   6:     <properties>
   7:     <add name="Groups" type="System.String" serializeAs="String" allowAnonymous="false" defaultValue="" readOnly="false"/>
   8:     . . .
   9:     </properties>
  10: </profile>

Data role yang disimpan pada atribut Groups di Profile akan dipisahkan oleh tanda koma (,). sehingga bila user punya role A dan B maka data yang disimpan pada atribut ini adalah A,B.

image

Misalnya role yang tersedia ditampilkan pada checkboxlist, maka penyimpanan role dari role yang dipilih pada checkboxlist dapat dilakukan dengan cara berikut :

   1: string roles = String.Empty;
   2: ProfileCommon profileCommon = Profile.GetProfile(userName);
   3:  
   4: for (int i = 0; i < CheckBoxList_FormRoles.Items.Count; i++)
   5: {
   6:     if (CheckBoxList_FormRoles.ItemsIdea.Selected)
   7:     {
   8:         if (!String.IsNullOrEmpty(roles)) roles += ",";
   9:         roles += CheckBoxList_FormRoles.ItemsIdea.Value;
  10:     }
  11:     CheckBoxList_FormRoles.ItemsIdea.Selected = false;
  12: }
  13: profileCommon.Groups = roles;
  14: profileCommon.Save();

{Menampilkan role yang ingin digunakan}

Setelah data telah siap, maka dapat dibuat skenario ketika user telah sukses login maka user akan ditampilkan role user yang disimpan pada Profile dan dapat dipilih seperti berikut ini.

image

Pada gambar di atas, dapat diketahui user tersebut mempunyai 4 role yang disimpan pada Profile, dan 1 role yang aktif yang disimpan pada tabel aspnet_UsersInRoles yaitu role Administrator (terlihat status = Active).

Untuk menampilkan 4 role yang disimpan pada Profile  tersebut, cukup buat GridView dan load datanya dengan cara seperti berikut ini :

   1: IList roles = new ArrayList();
   2: string username = HttpContext.Current.User.Identity.Name;
   3: ProfileCommon profileCommon = Profile.GetProfile(username);
   4: string rolesStr = profileCommon.Groups;
   5:  
   6: if (!String.IsNullOrEmpty(rolesStr))
   7: {
   8:     string[] roleArr = rolesStr.Split(',');
   9:     foreach (string role in roleArr)
  10:     {
  11:         CaltrisRoles item = new CaltrisRoles();
  12:         item.ROLENAME = role;
  13:         roles.Add(item);
  14:     }
  15: }
  16:  
  17: GridView_Roles.DataSource = roles;
  18: GridView_Roles.DataBind();

Dapat dilihat pada gambar di atas, terdapat LinkButton Select pada kolom Action.  Fungsi LinkButton itu ketika dipilih/diklik adalah menyimpan data role yang dipilih ke dalam tabel aspnet_UsersInRoles sesuai dengan user yang melakukan aksi tersebut.  Namun sebelumnya dipastikan terlebih dahulu data role dari user tersebut harus dihapus. Hal ini untuk memastikan data role yang disimpan untuk seorang user hanya SATU saja.

Cara untuk melakukan kebutuhan di atas dapat dilihat dari kode di bawah ini :

   1: string username = HttpContext.Current.User.Identity.Name;
   2: string selectedRole = Convert.ToString(GridView_Roles.SelectedValue);
   3: String[] roles = Roles.GetRolesForUser(username);
   4:  
   5: // remove role pada DB
   6: foreach (string role in roles)
   7: {
   8:     if (Roles.IsUserInRole(username, role))
   9:     {
  10:         Roles.RemoveUserFromRole(username, role);
  11:     }
  12: }
  13:  
  14: // insert role yang dipilih user pada GridView ke dalam DB
  15: Roles.AddUserToRole(username, selectedRole);

{Bukti}

Sebagai bukti dapat dilihat pada gambar berikut ini :

image

Pada gambar di atas role yang aktif adalah Administrator, maka dapat dilihat menu yang dimiliki oleh user tersebut. Dan berikut ini adalah ketika role FUEL yang aktif.

image

Sangat mudah bukan…terima kasih telah membaca, silakan kembali menikmati akhir weekend ini Smile

Share this post: | | | |
Posted: Apr 03 2011, 07:23 PM by reyza | with no comments
Filed under: ,