Bagikan melalui


Menavigasi dan Memperbarui Model dalam Kode Program

Anda dapat menulis kode untuk membuat dan menghapus elemen model, mengatur propertinya, serta membuat dan menghapus link antar elemen. Semua perubahan harus dilakukan dalam transaksi. Jika elemen ditampilkan pada diagram, diagram akan “diperbaiki” secara otomatis di akhir transaksi.

Contoh Definisi Bahasa Khusus Domain

Ini adalah bagian utama dari DslDefinition.dsl untuk contoh dalam topik ini:

DSL Definition diagram - family tree model

Model ini adalah instans bahasa khusus domain ini:

Tudor Family Tree Model

Referensi dan Namespace Layanan

Untuk menjalankan kode dalam topik ini, Anda harus mereferensikan:

Microsoft.VisualStudio.Modeling.Sdk.11.0.dll

Kode Anda akan menggunakan namespace layanan ini:

using Microsoft.VisualStudio.Modeling;

Selain itu, jika Anda menulis kode dalam proyek yang berbeda dari yang didefinisikan bahasa khusus domain Anda, Anda harus mengimpor rakitan yang dibangun oleh proyek Dsl.

Properti

Properti domain yang Anda tentukan dalam definisi bahasa khusus domain menjadi properti yang dapat Anda akses dalam kode program:

Person henry = ...;

if (henry.BirthDate < 1500) ...

if (henry.Name.EndsWith("VIII")) ...

Jika Anda ingin mengatur properti, Anda harus melakukannya di dalam transaksi:

henry.Name = "Henry VIII";

Jika dalam definisi bahasa khusus domain, Jenis properti Dihitung, Anda tidak dapat mengaturnya. Untuk informasi selengkapnya, lihat Properti Penyimpanan Terhitung dan Kustom.

Hubungan

Hubungan domain yang Anda tentukan dalam definisi bahasa khusus domain menjadi pasangan properti, satu pada kelas di setiap akhir hubungan. Nama properti muncul dalam diagram DslDefinition sebagai label pada peran di setiap sisi hubungan. Bergantung pada kardinalitas peran, jenis properti adalah kelas di akhir hubungan lainnya, atau kumpulan kelas tersebut.

foreach (Person child in henry.Children) { ... }

FamilyTreeModel ftree = henry.FamilyTreeModel;

Properti di akhir hubungan yang berlawanan selalu bersifat timbal balik. Ketika link dibuat atau dihapus, properti peran pada kedua elemen diperbarui. Ekspresi berikut (yang menggunakan ekstensi System.Linq) selalu benar untuk hubungan ParentsHaveChildren dalam contoh:

(Person p) => p.Children.All(child => child.Parents.Contains(p))

&& p.Parents.All(parent => parent.Children.Contains(p));

ElementLinks. Hubungan juga diwakili oleh elemen model yang disebut link, yang merupakan instans jenis hubungan domain. Link selalu memiliki satu elemen sumber dan satu elemen target. Elemen sumber dan elemen target bisa sama.

Anda dapat mengakses link dan propertinya:

ParentsHaveChildren link = ParentsHaveChildren.GetLink(henry, edward);

// This is now true:

link == null || link.Parent == henry && link.Child == edward

Secara default, tidak lebih dari satu instans hubungan diizinkan untuk menautkan sepasang elemen model. Namun, jika dalam definisi DSL, bendera Allow Duplicates benar untuk hubungan, maka mungkin ada lebih dari satu link, dan Anda harus menggunakan GetLinks:

foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinks(henry, edward)) { ... }

Ada juga metode lain untuk mengakses link. Misalnya:

foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinksToChildren(henry)) { ... }

Peran tersembunyi. Jika dalam definisi DSL, Apakah Properti Yang Dihasilkan adalah false untuk peran tertentu, maka tidak ada properti yang dihasilkan yang sesuai dengan peran tersebut. Namun, Anda masih dapat mengakses link dan melintasi link menggunakan metode hubungan:

foreach (Person p in ParentsHaveChildren.GetChildren(henry)) { ... }

Contoh yang paling sering digunakan adalah hubungan PresentationViewsSubject, yang menautkan elemen model ke bentuk yang menampilkannya pada diagram:

PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape

Direktori Elemen

Anda dapat mengakses semua elemen di penyimpanan menggunakan direktori elemen:

store.ElementDirectory.AllElements

Ada juga metode untuk menemukan elemen, seperti sebagai berikut:

store.ElementDirectory.FindElements(Person.DomainClassId);

store.ElementDirectory.GetElement(elementId);

Mengakses Informasi Kelas

Anda bisa mendapatkan informasi tentang kelas, hubungan, dan aspek lain dari definisi DSL. Misalnya:

DomainClassInfo personClass = henry.GetDomainClass();

DomainPropertyInfo birthProperty =

personClass.FindDomainProperty("BirthDate")

DomainRelationshipInfo relationship =

link.GetDomainRelationship();

DomainRoleInfo sourceRole = relationship.DomainRole[0];

Kelas leluhur elemen model adalah sebagai berikut:

  • ModelElement - semua elemen dan hubungan adalah ModelElements

  • ElementLink - semua hubungan adalah ElementLinks

Melakukan Perubahan di dalam Transaksi

Setiap kali kode program Anda mengubah apa pun di Store, kode tersebut harus dilakukan di dalam transaksi. Kode ini berlaku untuk semua elemen model, hubungan, bentuk, diagram, dan propertinya. Untuk informasi selengkapnya, lihat Transaction .

Metode paling nyaman untuk mengelola transaksi adalah dengan pernyataan using yang diapit dalam pernyataan try...catch:

Store store; ...
try
{
  using (Transaction transaction =
    store.TransactionManager.BeginTransaction("update model"))
    // Outermost transaction must always have a name.
  {
    // Make several changes in Store:
    Person p = new Person(store);
    p.FamilyTreeModel = familyTree;
    p.Name = "Edward VI";
    // end of changes to Store

    transaction.Commit(); // Don't forget this!
  } // transaction disposed here
}
catch (Exception ex)
{
  // If an exception occurs, the Store will be
  // rolled back to its previous state.
}

Anda dapat membuat sejumlah perubahan di dalam satu transaksi. Anda dapat membuka transaksi baru di dalam transaksi aktif.

Untuk membuat perubahan permanen, Anda harus Commit melakukan transaksi sebelum dibuang. Jika terjadi pengecualian yang tidak tertangkap di dalam transaksi, Store akan diatur ulang ke statusnya sebelum perubahan.

Membuat Elemen Model

Contoh ini menambahkan elemen ke model yang sudah ada:

FamilyTreeModel familyTree = ...; // The root of the model.
using (Transaction t =
    familyTree.Store.TransactionManager
    .BeginTransaction("update model"))
{
  // Create a new model element
  // in the same partition as the model root:
  Person edward = new Person(familyTree.Partition);
  // Set its embedding relationship:
  edward.FamilyTreeModel = familyTree;
          // same as: familyTree.People.Add(edward);
  // Set its properties:
  edward.Name = "Edward VII";
  t.Commit(); // Don't forget this!
}

Contoh ini mengilustrasikan poin-poin penting ini tentang membuat elemen:

  • Buat elemen baru dalam partisi tertentu dari Store. Untuk elemen dan hubungan model, tetapi bukan bentuk, ini biasanya merupakan partisi default.

  • Jadikan target hubungan penyematan. Dalam DslDefinition contoh ini, setiap Orang harus menjadi target hubungan penyematan FamilyTreeHasPeople. Untuk mencapai hal ini, kita dapat mengatur properti peran FamilyTreeModel dari objek Orang, atau menambahkan Orang ke properti peran Manusia dari objek FamilyTreeModel.

  • Atur properti elemen baru, terutama properti yang IsName adalah true di DslDefinition. Bendera ini menandai properti yang berfungsi untuk mengidentifikasi elemen secara unik dalam pemiliknya. Dalam hal ini, properti Nama memiliki bendera tersebut.

  • Definisi bahasa khusus domain dari bahasa khusus domain ini harus telah dimuat ke dalam Store. Jika Anda menulis ekstensi seperti perintah menu, ini biasanya sudah true. Dalam kasus lain, Anda dapat secara eksplisit memuat model ke Store, atau menggunakan ModelBus untuk memuatnya. Untuk mendapatkan informasi selengkapnya, lihat Cara: Membuka Model dari File dalam Kode Program.

    Ketika Anda membuat elemen dengan cara ini, bentuk secara otomatis dibuat (jika bahasa khusus domain memiliki diagram). Bentuk ini muncul di lokasi yang ditetapkan secara otomatis, dengan bentuk, warna, dan fitur default lainnya. Jika Anda ingin mengontrol tempat dan cara bentuk terkait muncul, lihat Membuat Elemen dan Bentuknya.

Ada dua hubungan yang ditentukan dalam contoh definisi bahasa khusus domain. Setiap hubungan mendefinisikan properti peran pada kelas di setiap akhir hubungan.

Ada tiga cara tempat Anda dapat membuat instans hubungan. Masing-masing dari ketiga metode ini memiliki efek yang sama:

  • Atur properti pemutar peran sumber. Misalnya:

    • familyTree.People.Add(edward);

    • edward.Parents.Add(henry);

  • Atur properti pemutar peran target. Misalnya:

    • edward.familyTreeModel = familyTree;

      Kardinalitas peran ini adalah 1..1, sehingga kami menetapkan nilainya.

    • henry.Children.Add(edward);

      Kardinalitas peran ini adalah 0..*, sehingga kami menambahkan ke koleksi.

  • Buat instans hubungan secara eksplisit. Misalnya:

    • FamilyTreeHasPeople edwardLink = new FamilyTreeHasPeople(familyTreeModel, edward);

    • ParentsHaveChildren edwardHenryLink = new ParentsHaveChildren(henry, edward);

    Metode terakhir berguna jika Anda ingin mengatur properti pada hubungan itu sendiri.

    Ketika Anda membuat elemen dengan cara ini, konektor pada diagram dibuat secara otomatis, tetapi memiliki bentuk, warna, dan fitur default lainnya. Untuk mengontrol cara konektor terkait dibuat, lihat Membuat Elemen dan Bentuknya.

Menghapus Elemen

Hapus elemen dengan memanggil Delete():

henry.Delete();

Operasi ini juga akan menghapus:

  • Link hubungan ke dan dari elemen. Contohnya, edward.Parents tidak akan lagi memuat henry.

  • Elemen pada peran yang bendera PropagatesDelete adalah true. Contohnya, bentuk yang menampilkan elemen akan dihapus.

Secara default, setiap hubungan penyematan memiliki PropagatesDelete true pada peran target. Menghapus henry tidak menghapus familyTree, tetapi familyTree.Delete() akan menghapus semua Persons.

Secara default, PropagatesDelete bukan true untuk peran hubungan referensi.

Anda dapat menyebabkan aturan penghapusan untuk menghilangkan penyebarluasan tertentu ketika Anda menghapus objek. Aturan ini berguna jika Anda mengganti satu elemen dengan elemen lain. Anda menyediakan GUID dari satu atau beberapa peran yang penghapusannya tidak boleh disebarluaskan. GUID dapat diperoleh dari kelas hubungan:

henry.Delete(ParentsHaveChildren.SourceDomainRoleId);

(Contoh khusus ini tidak akan berpengaruh, karena PropagatesDelete adalah false untuk peran hubungan ParentsHaveChildren.)

Dalam beberapa kasus, penghapusan dicegah oleh keberadaan kunci, baik pada elemen maupun pada elemen yang akan dihapus oleh penyebarluasan. Anda dapat menggunakan element.CanDelete() untuk memeriksa apakah elemen dapat dihapus.

Anda dapat menghapus link hubungan dengan menghapus elemen dari properti peran:

henry.Children.Remove(edward); // or:

edward.Parents.Remove(henry); // or:

Anda juga dapat menghapus link secara eksplisit:

edwardHenryLink.Delete();

Ketiga metode ini semuanya memiliki efek yang sama. Anda hanya perlu menggunakan salah satu metode.

Jika peran memiliki kardinalitas 0..1 atau 1..1, Anda dapat mengaturnya ke null, atau ke nilai lain:

edward.FamilyTreeModel = null; // atau:

edward.FamilyTreeModel = anotherFamilyTree;

Mengurutkan ulang Link Hubungan

Link hubungan tertentu yang bersumber atau ditargetkan pada elemen model tertentu memiliki urutan tertentu. Link tersebut muncul dalam urutan penambahannya. Contohnya, pernyataan ini akan selalu menangguhkan turunan dalam urutan yang sama:

foreach (Person child in henry.Children) ...

Anda dapat mengubah urutan link:

ParentsHaveChildren link = GetLink(henry,edward);

ParentsHaveChildren nextLink = GetLink(henry, elizabeth);

DomainRoleInfo role =

link.GetDomainRelationship().DomainRoles[0];

link.MoveBefore(role, nextLink);

Penguncian

Perubahan Anda dapat dicegah oleh kunci. Kunci dapat diatur pada masing-masing elemen, pada partisi, dan pada penyimpanan. Jika salah satu tingkat ini memiliki kunci yang mencegah jenis perubahan yang ingin Anda lakukan, pengecualian dapat dilemparkan ketika Anda mencobanya. Anda dapat menemukan apakah kunci diatur dengan menggunakan element.GetLocks(), yang merupakan metode ekstensi yang ditentukan dalam namespace layanan Microsoft.VisualStudio.Modeling.Immutability.

Untuk mendapatkan informasi selengkapnya, lihat Menentukan Kebijakan Penguncian untuk Membuat Segmen Baca-Saja.

Salin dan Tempel

Anda dapat menyalin elemen atau grup elemen ke IDataObject:

Person person = personShape.ModelElement as Person;
Person adopter = adopterShape.ModelElement as Person;
IDataObject data = new DataObject();
personShape.Diagram.ElementOperations
      .Copy(data, person.Children.ToList<ModelElement>());

Elemen disimpan sebagai Grup Elemen berseri.

Anda dapat menggabungkan elemen dari IDataObject ke dalam model:

using (Transaction t = targetDiagram.Store.
        TransactionManager.BeginTransaction("paste"))
{
  adopterShape.Diagram.ElementOperations.Merge(adopter, data);
}

Merge () dapat menerima PresentationElement atau ModelElement. Jika Anda memberinya PresentationElement, Anda juga dapat menentukan posisi pada diagram target sebagai parameter ketiga.

Menavigasi dan memperbarui diagram

Dalam bahasa khusus domain, elemen model domain, yang mewakili konsep seperti Orang atau Lagu, terpisah dari elemen bentuk, yang mewakili yang Anda lihat pada diagram. Elemen model domain menyimpan properti dan hubungan penting dari konsep. Elemen bentuk menyimpan ukuran, posisi, dan warna tampilan objek pada diagram, dan tata letak bagian komponennya.

Elemen Presentasi

Class diagram of base shape and element types

Dalam Definisi Bahasa Khusus Domain Anda, setiap elemen yang Anda tentukan membuat kelas yang berasal dari salah satu kelas standar berikut.

Jenis elemen Kelas dasar
Kelas domain ModelElement
Hubungan domain ElementLink
Bentuk NodeShape
Konektor BinaryLinkShape
Diagram Diagram

Elemen pada diagram biasanya mewakili elemen model. Biasanya (tetapi tidak selalu), NodeShape mewakili instans kelas domain, dan BinaryLinkShape mewakili instans hubungan domain. Hubungan PresentationViewsSubject ini menautkan simpul atau menautkan bentuk ke elemen model yang diwakilinya.

Setiap simpul atau bentuk link milik satu diagram. Bentuk link biner menyambungkan dua bentuk simpul.

Bentuk dapat memiliki bentuk turunan dalam dua kumpulan. Bentuk dalam kumpulan NestedChildShapes terbatas pada kotak pembatas induknya. Bentuk dalam daftar RelativeChildShapes dapat muncul di luar atau sebagian di luar batas induk - contohnya label atau port. Diagram tidak memiliki RelativeChildShapes dan tidak Parent.

Menavigasi antara bentuk dan elemen

Elemen model domain dan elemen bentuk terkait dengan hubungan PresentationViewsSubject.

// using Microsoft.VisualStudio.Modeling;
// using Microsoft.VisualStudio.Modeling.Diagrams;
// using System.Linq;
Person henry = ...;
PersonShape henryShape =
  PresentationViewsSubject.GetPresentation(henry)
    .FirstOrDefault() as PersonShape;

Hubungan yang sama menautkan hubungan ke konektor pada diagram:

Descendants link = Descendants.GetLink(henry, edward);
DescendantConnector dc =
   PresentationViewsSubject.GetPresentation(link)
     .FirstOrDefault() as DescendantConnector;
// dc.FromShape == henryShape && dc.ToShape == edwardShape

Hubungan ini juga menautkan akar model ke diagram:

FamilyTreeDiagram diagram =
   PresentationViewsSubject.GetPresentation(familyTree)
      .FirstOrDefault() as FamilyTreeDiagram;

Untuk mendapatkan elemen model yang diwakili oleh bentuk, gunakan:

henryShape.ModelElement as Person

diagram.ModelElement as FamilyTreeModel

Secara umum tidak disarankan untuk menavigasi antara bentuk dan konektor pada diagram. Lebih baik menavigasi hubungan dalam model, bergerak di antara bentuk dan konektor hanya ketika perlu untuk bekerja pada tampilan diagram. Metode ini menautkan konektor ke bentuk di setiap ujungnya:

personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes

connector.FromShape, connector.ToShape

Banyak bentuk berupa komposit; terdiri atas bentuk induk dan satu atau beberapa lapisan turunan. Bentuk yang diposisikan relatif terhadap bentuk lain dikatakan sebagai turunan. Ketika bentuk induk bergerak, turunan berpindah bersamanya.

Turunan relatif dapat muncul di luar kotak pembatas bentuk induk. Turunan bersarang muncul secara ketat di dalam batas induk.

Untuk mendapatkan kumpulan bentuk atas pada diagram, gunakan:

Diagram.NestedChildShapes

Kelas leluhur bentuk dan konektor adalah:

ModelElement

-- PresentationElement

-- ShapeElement

----- NodeShape

------- Diagram

------- YourShape

----- LinkShape

------- BinaryLinkShape

--------- YourConnector

Properti Bentuk dan Konektor

Dalam kebanyakan kasus, tidak perlu melakukan perubahan eksplisit pada bentuk. Ketika Anda telah mengubah elemen model, aturan “perbaiki” memperbarui bentuk dan konektor. Untuk mendapatkan informasi selengkapnya, lihat Merespons dan Menyebarluaskan Perubahan.

Namun, berguna untuk melakukan beberapa perubahan eksplisit pada bentuk dalam properti yang independen dari elemen model. Contohnya, Anda dapat mengubah properti-properti ini:

  • Size - menentukan tinggi dan lebar bentuk.

  • Location - posisi relatif terhadap bentuk atau diagram induk

  • StyleSet - kumpulan pena dan kuas yang digunakan untuk menggambar bentuk atau konektor

  • Hide - membuat bentuk tidak terlihat

  • Show - membuat bentuk terlihat setelah Hide()

Membuat Elemen dan Bentuknya

Ketika Anda membuat elemen dan menautkannya ke pohon hubungan penyematan, bentuk secara otomatis dibuat dan dikaitkan dengannya. Hal ini dilakukan melalui aturan “perbaikan” yang dijalankan pada akhir transaksi. Namun, bentuk akan muncul di lokasi yang ditetapkan secara otomatis, dan bentuk, warna, dan fitur lainnya akan memiliki nilai default. Untuk mengontrol cara bentuk dibuat, Anda dapat menggunakan fungsi penggabungan. Anda harus terlebih dahulu menambahkan elemen yang ingin Anda tambahkan ke dalam ElementGroup, lalu menggabungkan grup ke dalam diagram.

Metode ini:

  • Mengatur nama, jika Anda telah menetapkan properti sebagai nama elemen.

  • Mengamati Arahan Gabungan Elemen apa pun yang Anda tentukan dalam Definisi Bahasa Khusus Domain.

Contoh ini membuat bentuk pada posisi mouse, ketika pengguna mengeklik dua kali diagram. Dalam Definisi Bahasa Khusus Domain untuk sampel ini, properti FillColor dari ExampleShape telah diekspos.

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
partial class MyDiagram
{
  public override void OnDoubleClick(DiagramPointEventArgs e)
  {
    base.OnDoubleClick(e);

    using (Transaction t = this.Store.TransactionManager
        .BeginTransaction("double click"))
    {
      ExampleElement element = new ExampleElement(this.Store);
      ElementGroup group = new ElementGroup(element);

      { // To use a shape of a default size and color, omit this block.
        ExampleShape shape = new ExampleShape(this.Partition);
        shape.ModelElement = element;
        shape.AbsoluteBounds = new RectangleD(0, 0, 1.5, 1.0);
        shape.FillColor = System.Drawing.Color.Azure;
        group.Add(shape);
      }

      this.ElementOperations.MergeElementGroupPrototype(
        this,
        group.CreatePrototype(),
        PointD.ToPointF(e.MousePosition));
      t.Commit();
    }
  }
}

Jika Anda menyediakan lebih dari satu bentuk, atur posisi relatifnya menggunakan AbsoluteBounds.

Anda juga dapat mengatur warna dan properti konektor lain yang diekspos menggunakan metode ini.

Penggunaan Transaksi

Bentuk, konektor, dan diagram adalah subjenis ModelElement dan bersifat langsung di Store. Oleh karena itu, Anda harus melakukan perubahan hanya di dalam transaksi. Untuk mendapatkan informasi selengkapnya, lihat Cara: Menggunakan Transaksi untuk Memperbarui Model.

Tampilan Dokumen dan Data Dokumen

Class diagram of standard diagram types

Partisi Store

Ketika model dimuat, diagram yang menyertainya dimuat secara bersamaan. Biasanya, model dimuat ke Store.DefaultPartition, dan konten diagram dimuat ke Partisi lain. Biasanya, konten setiap partisi dimuat dan disimpan ke file terpisah.