This is an example (a rather long one at that). It has been my experience to
learn the "why" and "what" of a technology is just as important as "how."
You need to know why you would want or need a distributed transaction before
you go off and see how to do it. ES/COM+ is also a lot more than this.
Without the need for Distributed Transactions, and he doesn't, basic COM
Interop applies and the article I cited explains the "why" of COM Interop
and then an example on how to do it.
--
--------------------------------------------------------------
Sam Gentile [C#/.NET MVP]
..NET Blog
http://samgentile.com/blog/
MSDN Column:
http://msdn.microsoft.com/library/de...tml/bridge.asp
Please reply only to the newsgroup so that others can benefit.
This posting is provided "AS IS" with no warranties, and confers no rights.
---------------------------------------------------------------
"UAError" <null@null.null> wrote in message
news:pjh220hfhk478ef7k9jiodi34tlc3osn9n@4ax.com...[color=blue]
> "Lasse Edsvik" <lasse@nospam.com> wrote:
>[color=green]
> >in this example i dont have to use transactions..... so i'll skip that[/color][/color]
and[color=blue][color=green]
> >learn that some other time then...
> >[/color]
>
> Serviced components are a bit harder to explain:
>
> The following is an example from Amit Kalani's book for 70-320
> It assumes that you have SQL Server with the Northwind database running[/color]
locally[color=blue]
>
> You can always get this:
>
> Microsoft SQL Server 2000 Desktop Engine (MSDE 2000) Release A
>[/color]
http://www.microsoft.com/downloads/d...displaylang=en[color=blue]
>
>
> The example involves to serviced components one for shipping, one for[/color]
billing which[color=blue]
> are used by an ordering component and finally a windows form client to use[/color]
them.[color=blue]
>
> Start by creating a blank solution in Visual Studio: ESTransApp
> (File->New->Blank Solution)
>
> CREATE NEW TABLES
> -----------------
>
> Open a Visual Studio .NET Command Prompt Window
> Navigate to the project directory "ESTransApp" and create a file[/color]
"ShipBill.sql" with the following contents:[color=blue]
>
> SET quoted_identifier on
> USE "Northwind"
> GO
> IF EXISTS (SELECT 1 FROM sysobjects WHERE id = object_id('dbo.Shipping')[/color]
and sysstat & 0xf = 3)[color=blue]
> drop table "dbo"."Shipping"
> GO
> IF EXISTS (SELECT 1 FROM sysobjects WHERE id = object_id('dbo.Billing')[/color]
and sysstat & 0xf = 3)[color=blue]
> drop table "dbo"."Billing"
> GO
> CREATE TABLE "Shipping" (
> "ShippingID" "int" IDENTITY (1, 1) NOT NULL,
> "CustomerID" "nchar" (5) NOT NULL ,
> "ProductID" "int" NOT NULL ,
> "DateTime" "datetime" NOT NULL,
> CONSTRAINT "PK_Shipping" PRIMARY KEY CLUSTERED (
> "ShippingID"
> ),
> CONSTRAINT "FK_Shipping_Customers" FOREIGN KEY (
> "CustomerID"
> ) REFERENCES "dbo"."Customers" (
> "CustomerID"
> ),
> CONSTRAINT "FK_Shipping_Products" FOREIGN KEY (
> "ProductID"
> ) REFERENCES "dbo"."Products" (
> "ProductID"
> )
> )
> GO
> CREATE TABLE "Billing" (
> "BillingID" "int" IDENTITY (1, 1) NOT NULL,
> "CustomerID" "nchar" (5) NOT NULL ,
> "ProductID" "int" NOT NULL ,
> "DateTime" "datetime" NOT NULL,
> CONSTRAINT "PK_Billing" PRIMARY KEY CLUSTERED (
> "BillingID"
> ),
> CONSTRAINT "FK_Billing_Customers" FOREIGN KEY (
> "CustomerID"
> ) REFERENCES "dbo"."Customers" (
> "CustomerID"
> ),
> CONSTRAINT "FK_Billing_Products" FOREIGN KEY (
> "ProductID"
> ) REFERENCES "dbo"."Products" (
> "ProductID"
> )
> )
> GO
>
>
>
> The in the command prompt window run the following command:
>
> osql -E -i ShipBill.sql
>
> This should create the new tables
>
>
> CREATE Strong Name key
> ----------------------
> The in the command prompt window navigate to the solution directory[/color]
"ESTransApp"[color=blue]
> and the strong name key file "ESTransApp.snk":
>
> sn -k ESTransApp.snk
>
>
> CREATE Shipping Component
> -------------------------
>
> 1) Add a new Visual C#: Class Library "ShipTrans" to your "ESTransApp"[/color]
Solution.[color=blue]
>
> 2) Add a reference to enterprise services. Right-Click the project
> "Add Reference->.NET System.Enterprise.Services"
> Click "Select", Click "OK"
>
> 3) Rename the file "Shipping.cs" and enter the following:
>
> using System;
> using System.Data;
> using System.Data.SqlClient;
> using System.Data.SqlTypes;
> using System.EnterpriseServices;
> using System.Runtime.InteropServices;
>
> namespace ShipTrans {
>
> public interface IShipping {
> void ShipItem( string customerID, int productID );
> }
>
> // Get your own GUID: Tools->Create GUID "Registry Format" - Copy
> [Transaction(TransactionOption.Supported)]
> [ClassInterface(ClassInterfaceType.None)]
> [Guid("8D8B0556-FB6B-4ce6-948C-20F158987236")]
> public class Shipping : ServicedComponent, IShipping {
>
> SqlConnection cnn_ = null;
>
> public Shipping() {
> cnn_ = new SqlConnection(
> "data source=(local)"
> + ";initial catalog=Northwind"
> + ";integrated security=SSPI"
> );
> }
>
> #region IShipping Members
> [AutoComplete(true)]
> public void ShipItem(string customerID, int productID) {
> SqlDateTime dt = new SqlDateTime( DateTime.Now );
> SqlCommand cmd = cnn_.CreateCommand();
> cmd.CommandText = string.Format(
> "INSERT INTO Shipping ("
> + "CustomerID, ProductID, DateTime"
> + ") VALUES ("
> + "'{0}','{1}','{2}'"
> + ")",
> customerID,
> productID,
> dt
> );
> cnn_.Open();
> cmd.ExecuteNonQuery();
> }
> #endregion
> }
> }
>
>
> 4) Add the following at the top to the AssemblyInfo.cs file of the[/color]
"ShipTrans" project:[color=blue]
>
> using System.EnterpriseServices;
>
> and modify (or add) the following in AssemblyInfo.cs
>
> [assembly: ApplicationName("Shipping Application")]
> [assembly: Description("Ship Orders")]
> [assembly: ApplicationActivation(ActivationOption.Server)]
> [assembly: AssemblyVersion("1.0.0")]
> [assembly: AssemblyKeyFile("../../../ESTransApp.snk")]
> [assembly: ApplicationAccessControl(false)]
>
>
> 5) Build the project
>
> 6) Install the component to the Global assembly Cache (not technically[/color]
required but good practice)[color=blue]
> The in the command prompt window navigate to the project output directory[/color]
"ESTransApp\ShipTrans\bin\Debug"[color=blue]
> and issue the following command:
>
> gacutil -i ShipTrans.dll
>
> 7) Register the serviced component with the COM+ Catalog:
> The in the command prompt window navigate to the project output directory[/color]
"ESTransApp\ShipTrans\bin\Debug"[color=blue]
> and issue the following command:
>
> regsvcs ShipTrans.dll
>
> Ignore - WARNING: The class 'ShipTrans.Shipping' has no class interface,[/color]
which means that unmanaged[color=blue]
> late bound calls cannot take advantage of AutoComplete methods.
>
> 8) Check Serviced components.
> Open the "Component Services" MMI plugin (Control Panel->Adminstrative[/color]
Tools->Component Services) or[color=blue]
> on the command line:
>
> %SystemRoot%\system32\Com\comexp.msc
>
> Drill down
>
> Component Services->Computers->My Computers->COM+ Applications
>
> One of the applications should state "Shipping Application" - the recently[/color]
registered serviced component.[color=blue]
>
>
> CREATE Billing Component
> -------------------------
>
> 1) Add a new Visual C#: Class Library "BillTrans" to your "ESTransApp"[/color]
Solution.[color=blue]
>
> 2) Add a reference to enterprise services. Right-Click the project
> "Add Reference->.NET System.Enterprise.Services"
> Click "Select", Click "OK"
>
> 3) Rename the file "Billing.cs" and enter the following:
>
>
> using System;
> using System.Data;
> using System.Data.SqlClient;
> using System.Data.SqlTypes;
> using System.EnterpriseServices;
> using System.Runtime.InteropServices;
>
> namespace BillTrans {
>
> public interface IBilling {
> void BillCustomer( string customerID, int productID );
> }
>
> // Get your own GUID: Tools->Create GUID "Registry Format" - Copy
> [Transaction(TransactionOption.Supported)]
> [ClassInterface(ClassInterfaceType.None)]
> [Guid("2B2A9DE2-2CD9-40cf-BA34-2323B52B0F31")]
> public class Billing : ServicedComponent, IBilling {
>
> SqlConnection cnn_ = null;
>
> public Billing() {
> cnn_ = new SqlConnection(
> "data source=(local)"
> + ";initial catalog=Northwind"
> + ";integrated security=SSPI"
> );
> }
>
> #region IBilling Members
> [AutoComplete(true)]
> public void BillCustomer(string customerID, int productID) {
> SqlDateTime dt = new SqlDateTime( DateTime.Now );
> SqlCommand cmd = cnn_.CreateCommand();
> cmd.CommandText = string.Format(
> "INSERT INTO Billing ("
> + "CustomerID, ProductID, DateTime"
> + ") VALUES ("
> + "'{0}','{1}','{2}'"
> + ")",
> customerID,
> productID,
> dt
> );
> cnn_.Open();
> cmd.ExecuteNonQuery();
> }
> #endregion
> }
> }
>
>
> 4) Add the following at the top to the AssemblyInfo.cs file of the[/color]
"BillTrans" project:[color=blue]
>
> using System.EnterpriseServices;
>
> and modify (or add) the following in AssemblyInfo.cs
>
> [assembly: ApplicationName("Billing Application")]
> [assembly: Description("Bill Customers")]
> [assembly: ApplicationActivation(ActivationOption.Server)]
> [assembly: ApplicationAccessControl(false)]
> [assembly: AssemblyVersion("1.0.0")]
> [assembly: AssemblyKeyFile("../../../ESTransApp.snk")]
>
> 5) Build the project
>
> 6) Install the component to the Global assembly Cache (not technically[/color]
required but good practice)[color=blue]
> The in the command prompt window navigate to the project output directory[/color]
"ESTransApp\BillTrans\bin\Debug"[color=blue]
> and issue the following command:
>
> gacutil -i BillTrans.dll
>
> 7) Register the serviced component with the COM+ Catalog:
> The in the command prompt window navigate to the project output directory[/color]
"ESTransApp\BillTrans\bin\Debug"[color=blue]
> and issue the following command:
>
> regsvcs BillTrans.dll
>
> Ignore - WARNING: The class 'OrderTrans.Shipping' has no class interface,[/color]
which means that unmanaged[color=blue]
> late bound calls cannot take advantage of AutoComplete methods.
>
> 8) Check Serviced components.
> Open the "Component Services" MMI plugin (Control Panel->Adminstrative[/color]
Tools->Component Services) or[color=blue]
> on the command line:
>
> %SystemRoot%\system32\Com\comexp.msc
>
> Drill down
>
> Component Services->Computers->My Computers->COM+ Applications
>
> One of the applications should state "Billing Application" - the recently[/color]
registered serviced component.[color=blue]
>
>
> CREATE Ordering Component
> -------------------------
> 1) Add a new Visual C#: Class Library "OrderTrans" to your "ESTransApp"[/color]
Solution.[color=blue]
>
> 2) Add a reference to enterprise services. Right-Click the project
> "Add Reference->.NET System.Enterprise.Services"
> Click "Select", Click "OK"
>
> 3) Add references to the other projects. Right-Click the "OrderTrans"[/color]
project[color=blue]
> "Add Reference->Projects BillTrans"
> Click "Select"
> "Add Reference->Projects ShipTrans"
> Click "Select"
> Click "OK"
>
>
> 4) Rename the file "Ordering.cs" and enter the following:
>
>
> using System;
> using System.EnterpriseServices;
> using System.Runtime.InteropServices;
> using ShipTrans;
> using BillTrans;
>
> namespace OrderTrans {
>
> public interface IOrdering {
> void PlaceOrder( string customerID, int productID );
> }
>
> // Get your own GUID: Tools->Create GUID "Registry Format" - Copy
> [Transaction(TransactionOption.RequiresNew)]
> [ClassInterface(ClassInterfaceType.None)]
> [Guid("D229FB99-2FC7-4961-94FA-981E747F215B")]
> public class Ordering : ServicedComponent, IOrdering {
>
> public Ordering() {}
>
> #region IOrdering Members
> [AutoComplete(true)]
> public void PlaceOrder(string customerID, int productID) {
> Billing billing = new Billing();
> billing.BillCustomer( customerID, productID );
>
> Shipping shipping = new Shipping();
> shipping.ShipItem( customerID, productID );
> }
> #endregion
> }
> }
>
> 5) Add the following at the top to the AssemblyInfo.cs file of the[/color]
"OrderTrans" project:[color=blue]
>
> using System.EnterpriseServices;
>
> and modify (or add) the following in AssemblyInfo.cs
>
> [assembly: ApplicationName("Ordering Application")]
> [assembly: Description("Places an Order")]
> [assembly: ApplicationActivation(ActivationOption.Server)]
> [assembly: ApplicationAccessControl(false)]
> [assembly: AssemblyVersion("1.0.0")]
> [assembly: AssemblyKeyFile("../../../ESTransApp.snk")]
>
> 6) Build the project
>
> 7) Install the component to the Global assembly Cache (not technically[/color]
required but good practice)[color=blue]
> The in the command prompt window navigate to the project output directory[/color]
"ESTransApp\OrderTrans\bin\Debug"[color=blue]
> and issue the following command:
>
> gacutil -i OrderTrans.dll
>
> 8) Register the serviced component with the COM+ Catalog:
> The in the command prompt window navigate to the project output directory[/color]
"ESTransApp\BillTrans\bin\Debug"[color=blue]
> and issue the following command:
>
> regsvcs OrderTrans.dll
>
> Ignore - WARNING: The class 'OrderTrans.Shipping' has no class interface,[/color]
which means that unmanaged[color=blue]
> late bound calls cannot take advantage of AutoComplete methods.
>
> 9) Check Serviced components.
> Open the "Component Services" MMI plugin (Control Panel->Adminstrative[/color]
Tools->Component Services) or[color=blue]
> on the command line:
>
> %SystemRoot%\system32\Com\comexp.msc
>
> Drill down
>
> Component Services->Computers->My Computers->COM+ Applications
>
> One of the applications should state "Ordering Application" - the recently[/color]
registered serviced component.[color=blue]
>
>
> CREATE Serviced Components Client (Windows Forms)
> -------------------------------------------------
>
> 1) Add a new Visual C#: Windows Application "OrderApp" to your[/color]
"ESTransApp" Solution.[color=blue]
> Right-Click the project and click "Set As Start-up Project".
>
> 2) Add a reference to enterprise services. Right-Click the project
> "Add Reference->.NET System.Enterprise.Services"
> Click "Select", Click "OK"
>
> 3) Add references to the "OrderTrans" project. Right-Click the "OrderApp"[/color]
project[color=blue]
> "Add Reference->Projects OrderTrans"
> Click "Select"
> Click "OK"
>
> 4) Rename the file "Form1.cs" to "OrderForm.cs" and enter the following:
>
> using System;
> using System.Drawing;
> using System.Collections;
> using System.ComponentModel;
> using System.Windows.Forms;
> using System.Data;
> using System.Data.SqlClient;
> using OrderTrans;
>
> namespace OrderApp {
>
> public class OrderForm : System.Windows.Forms.Form {
>
> private System.Windows.Forms.GroupBox gbOrderInfo;
> private System.Windows.Forms.Label lblCustomer;
> private System.Windows.Forms.Label lblProduct;
> private System.Windows.Forms.Button btnOrder;
> private System.ComponentModel.Container components = null;
> private System.Windows.Forms.ComboBox cboProducts;
> private System.Windows.Forms.ComboBox cboCustomers;
>
> private DataSet dsInfo_ = null;
>
> public OrderForm () {
> InitializeComponent();
> }
>
> protected override void Dispose( bool disposing ) {
> if( disposing ) {
> if (components != null) {
> components.Dispose();
> }
> }
> base.Dispose( disposing );
> }
>
> #region Windows Form Designer generated code
> /// <summary>
> /// Required method for Designer support - do not modify
> /// the contents of this method with the code editor.
> /// </summary>
> private void InitializeComponent()
> {
> this.gbOrderInfo = new System.Windows.Forms.GroupBox();
> this.lblProduct = new System.Windows.Forms.Label();
> this.lblCustomer = new System.Windows.Forms.Label();
> this.cboCustomers = new System.Windows.Forms.ComboBox();
> this.cboProducts = new System.Windows.Forms.ComboBox();
> this.btnOrder = new System.Windows.Forms.Button();
> this.gbOrderInfo.SuspendLayout();
> this.SuspendLayout();
> //
> // gbOrderInfo
> //
> this.gbOrderInfo.Controls.Add(this.btnOrder);
> this.gbOrderInfo.Controls.Add(this.cboProducts);
> this.gbOrderInfo.Controls.Add(this.lblProduct);
> this.gbOrderInfo.Controls.Add(this.lblCustomer);
> this.gbOrderInfo.Controls.Add(this.cboCustomers);
> this.gbOrderInfo.Location = new System.Drawing.Point(8, 8);
> this.gbOrderInfo.Name = "gbOrderInfo";
> this.gbOrderInfo.Size = new System.Drawing.Size(336, 136);
> this.gbOrderInfo.TabIndex = 0;
> this.gbOrderInfo.TabStop = false;
> this.gbOrderInfo.Text = "Ordering Information";
> //
> // lblProduct
> //
> this.lblProduct.Location = new System.Drawing.Point(16, 57);
> this.lblProduct.Name = "lblProduct";
> this.lblProduct.Size = new System.Drawing.Size(64, 23);
> this.lblProduct.TabIndex = 2;
> this.lblProduct.Text = "&Product";
> this.lblProduct.TextAlign =[/color]
System.Drawing.ContentAlignment.MiddleRight;[color=blue]
> //
> // lblCustomer
> //
> this.lblCustomer.Location = new System.Drawing.Point(16, 25);
> this.lblCustomer.Name = "lblCustomer";
> this.lblCustomer.Size = new System.Drawing.Size(64, 23);
> this.lblCustomer.TabIndex = 0;
> this.lblCustomer.Text = "&Customer";
> this.lblCustomer.TextAlign =[/color]
System.Drawing.ContentAlignment.MiddleRight;[color=blue]
> //
> // cboCustomers
> //
> this.cboCustomers.Location = new System.Drawing.Point(96, 24);
> this.cboCustomers.Name = "cboCustomers";
> this.cboCustomers.Size = new System.Drawing.Size(216, 24);
> this.cboCustomers.TabIndex = 1;
> //
> // cboProducts
> //
> this.cboProducts.Location = new System.Drawing.Point(96, 56);
> this.cboProducts.Name = "cboProducts";
> this.cboProducts.Size = new System.Drawing.Size(216, 24);
> this.cboProducts.TabIndex = 3;
> //
> // btnOrder
> //
> this.btnOrder.Location = new System.Drawing.Point(104, 96);
> this.btnOrder.Name = "btnOrder";
> this.btnOrder.Size = new System.Drawing.Size(96, 23);
> this.btnOrder.TabIndex = 4;
> this.btnOrder.Text = "Place Order";
> this.btnOrder.Click += new[/color]
System.EventHandler(this.btnOrder_Click);[color=blue]
> //
> // OrderForm
> //
> this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
> this.ClientSize = new System.Drawing.Size(352, 153);
> this.Controls.Add(this.gbOrderInfo);
> this.Name = "OrderForm";
> this.Text = "Order Form";
> this.Load += new System.EventHandler(this.OrderForm_Load);
> this.gbOrderInfo.ResumeLayout(false);
> this.ResumeLayout(false);
>
> }
> #endregion
>
> [STAThread]
> static void Main() {
> Application.Run(new OrderForm());
> }
>
> private void OrderForm_Load(object sender, System.EventArgs e) {
>
> SqlConnection cnn = new SqlConnection(
> "data source=(local)"
> + ";initial catalog=Northwind"
> + ";integrated security=SSPI"
> );
>
> // Load Customers
> SqlCommand cmdCustomers = cnn.CreateCommand();
> cmdCustomers.CommandType = CommandType.Text;
> cmdCustomers.CommandText =
> "SELECT CompanyName, CustomerID FROM Customers";
>
> SqlDataAdapter daCustomers = new SqlDataAdapter();
> daCustomers.SelectCommand = cmdCustomers;
>
> // Load Products
> SqlCommand cmdProducts = cnn.CreateCommand();
> cmdProducts.CommandType = CommandType.Text;
> cmdProducts.CommandText =
> "SELECT ProductName, ProductID FROM Products";
>
> SqlDataAdapter daProducts = new SqlDataAdapter();
> daProducts.SelectCommand = cmdProducts;
>
> dsInfo_ = new DataSet();
>
> daCustomers.Fill( dsInfo_, "Customers" );
> daProducts.Fill( dsInfo_, "Products" );
>
> // Bind comboboxes
> cboCustomers.DataSource = dsInfo_;
> cboCustomers.DisplayMember = "Customers.CompanyName";
> cboCustomers.ValueMember = "Customers.CustomerID";
>
> cboProducts.DataSource = dsInfo_;
> cboProducts.DisplayMember = "Products.ProductName";
> cboProducts.ValueMember = "Products.ProductID";
>
> } // end event handler OrderForm_Load
>
> private void btnOrder_Click(object sender, System.EventArgs e) {
> try {
> // Ordering Serviced component is used here
> Ordering ordering = new Ordering();
> ordering.PlaceOrder(
> cboCustomers.SelectedValue.ToString(),
> (int) cboProducts.SelectedValue
> );
> MessageBox.Show( "Order Placed Successfully" );
>
>
> } catch ( Exception ex ) {
> MessageBox.Show( ex.Message );
> }
> } // end event handler btnOrder_Click
> }
> }
>
>
> 5) Build the project
>
> 6) Run OrderApp. You should be able to place an order.
>
> 7) Now go into the "Component Services" console and disable the "Shipping[/color]
Application".[color=blue]
> If you try to place an order now, an exception will be thrown and the[/color]
transaction is aborted;[color=blue]
> i.e. there will be no Billing record without a shipping record.[/color]