Thursday, November 22, 2012

Abstract Class vs Interface


Abstract Class vs Interface
I am assuming you are having all the basic knowledge of abstract and interface keyword. I am just briefing the basics.
We can not make instance of Abstract Class as well as Interface.
Here are few differences in Abstract class and Interface as per the definition.
Abstract class can contain abstract methods, abstract property as well as other members (just like normal class).
Interface can only contain abstract methods, properties but we don’t need to put abstract and public keyword. All the methods and properties defined in Interface are by default public and abstract.

            //Abstarct Class
public abstract class Vehicles
      {
        private int noOfWheel;
        private string color;
        public abstract string Engine
        {  
            get;
            set;
        }
        public abstract void Accelerator();
      }
      //Interface
public interface Vehicles
      {
        string Engine
        {  
            get;
            set;
        }
        void Accelerator();
      }

We can see abstract class contains private members also we can put some methods with implementation also. But in case of interface only methods and properties allowed.
We use abstract class and Interface for the base class in our application.

This is all about the language defination. Now million doller question:
How can we take decision about when we have to use Interface and when Abstract Class.
Basicly abstact class is a abstract view of any realword entity and interface is more abstract one. When we thinking about the entity there are two things one is intention and one is implemntation. Intention means I know about the entity and  also may have idea about its state as well as behaviour but don’t know about how its looks or works or may know partially. Implementation means actual state and behaviour of entity.  
Enough theory lets take an example.
I am trying to make a Content Management System where content is a genralize form of article, reviews, blogs etc.
                                   
So content is our base class now how we make a decision whether content class should be Abstract class, Interface or normal class.
First normal class vs other type (abstract and interface). If content is not a core entity of my application means as per the business logic if content is nothing in my application only Article, Blogs, Review are the core part of business logic then content class should not be a normal class  because I’ll never make instance of that class. So if you will never make instance of base class then Abstract class and Interface are the more appropriate choice.
Second between Interface and Abstract Class.
CONTENT
Publish ()

ARTICLE

BLOGS

REVIEW
                                                      


As you can see content having behavior named “Publish”. If according to my business logic Publish having some default behavior which apply to all I’ll prefer content class as an Abstract class. If there is no default behavior for the “Publish” and every drive class makes their own implementation then there is no need to implement “Publish” behavior in  the base case I’ll prefer Interface.
These are the in general idea of taking decision between abstract class, interface and normal class. But there is one catch. As we all know there is one constant in software that is “CHANGE”. If I made content class as Interface then it is difficult to make changes in base class because if I add new method or property in content interface then I have to implement new method in every drive class. These problems will over come if you are using abstract class for content class and new method is not an abstract type. So we can replace interface with abstract class except multiple inheritance.
CAN-DO and IS-A relationship is also define the deference between Interface and abstract class. As we already discuss Interface can be use for multiple inheritance for example we have another interface named “ICopy” which having behavior copy and every drive class have to implements its own implementation of Copy. If “Article” class drive from abstract class Content as well as ICopy then article “CAN-DO” copy also.
IS-A is for “generalization” and “specialization” means content is a generalize form of Article, Blogs, Review and Article, Blogs, Review are a specialize form of Content.
So, abstract class defines core identity. If we are thinking in term of speed then abstract is fast then interface because interface requires extra in-direction.
So as per my view Abstract class having upper-hand in compare to interface. Using  interface having only advantage of multiple inheritance. If you don’t understand the things then don’t worry because it’s my mistake because I am not able to describe the topic.

FeatureInterfaceAbstract class
Multiple inheritanceA class may inherit several interfaces.A class may inherit only one abstract class.
Default implementationAn interface cannot provide any code, just the signature.An abstract class can provide complete, default code and/or just the details that have to be overridden.
Access ModfiersAn interface cannot have access modifiers for the subs, functions, properties etc everything is assumed as publicAn abstract class can contain access modifiers for the subs, functions, properties
Core VS PeripheralInterfaces are used to define the peripheral abilities of a class. In other words both Human and Vehicle can inherit from a IMovable interface.An abstract class defines the core identity of a class and there it is used for objects of the same type.
HomogeneityIf various implementations only share method signatures then it is better to use Interfaces.If various implementations are of the same kind and use common behaviour or status then abstract class is better to use.
SpeedRequires more time to find the actual method in the corresponding classes.Fast
Adding functionality (Versioning)If we add a new method to an Interface then we have to track down all the implementations of the interface and define implementation for the new method.If we add a new method to an abstract class then we have the option of providing default implementation and therefore all the existing code might work properly.
Fields and ConstantsNo fields can be defined in interfacesAn abstract class can have fields and constrants defined

  • An Interface cannot implement methods.
  • An abstract class can implement methods.

  • An Interface can only inherit from another Interface.
  • An abstract class can inherit from a class and one or more interfaces.

  • An Interface cannot contain fields.
  • An abstract class can contain fields.

  • An Interface can contain property definitions.
  • An abstract class can implement a property.

  • An Interface cannot contain constructors or destructors.
  • An abstract class can contain constructors or destructors.

  • An Interface can be inherited from by structures.
  • An abstract class cannot be inherited from by structures.

  • An Interface can support multiple inheritance.
  • An abstract class cannot support multiple inheritance.

Friday, November 16, 2012

Understanding Delegates

A delegate is an important element of C# and is extensively used in every type of .NET application. A delegate is a class whose object (delegate object) can store a set of references to methods. This delegate object is used to invoke the methods. Many developers find delegates complicated. But believe me, it’s a simple concept to understand. In this article we will see how delegates work and in what situations they are used.
Let’s start with a simple program that shows how to declare and use a delegate.


class sample
{
  delegate void del1();
  delegate void del2 (int i);
  public void fun()
  {
   del1 d1 = new del1 (f1);
   d1();
   del2 d2 = new del2 (f2) ;
   d2 ( 5 ) ;
  }
  public void f1()
  {
   Console.WriteLine (“Reached in f1”) ;
  }
  public void f2 (int i)
  {
   Console.WriteLine (“Reached in f2”) ;
  }
}
In the sample class we have declared two delegates—del1 and del2. The statement delegate void del1( ) ;
indicates that the delegate del1 is going to encapsulate methods, which takes no parameter and returns void. When this statement is encountered, a class del1 derived from a pre-defined class MultiCastDelegate gets created. Similarly, the statement delegate void del2 ( int i ) ; indicates that the delegate del2 will be used for methods taking one integer and returning a void. This statement would create another class del2 derived from the MultiCastDelegate class. The classes del1 and del2 are called ‘delegate classes’. To use the delegate class, like any other class, we have to declare its reference. We have declared the reference to delegate class del1 in the fun( ) method through the statement del1 d1 = new del1 ( f1 ) ;
This would create a delegate object d1. To the constructor of the delegate class we have passed the address of the method f1( ). C++ programmers may know that mentioning a method name without parentheses represents its address. The delegate object would now hold an address of the method f1( ). Next, we have called the f1( ) method using the delegate object d1. This is achieved using the statement d1( ) ;
If d1 is an object, how can we use it as if it is a method? Actually, the call d1( ) gets converted into a call to the d1.Invoke( ) method. The Invoke( ) method calls the f1( ) method using its address stored in delegate object. A delegate can contain references to multiple methods. In this case a single call to Invoke( ) calls all the methods.
Similar to d1 we have instantiated another delegate object d2 and stored in it an address of the method f2( ). As the method f2( ) takes an integer as a parameter we pave passed 5 to d2( ). The value 5 would get passed to all the methods whose references are stored in this delegate object. This makes it obvious that the signature of the delegate and that of the methods to be invoked using the delegate must be identical.
If we create an object of sample class and call the fun( ) method both the f1( ) and f2( ) methods would get called through d1 and d2 respectively.
Delegates make possible calling of methods using reference to methods the object oriented way. This avoids using any complex technique like pointers to functions.
Delegates in Inheritance
Suppose there is a base class and a class derived from this class. A method is invoked from a base class method using a delegate. We can initialise this delegate in derived class so that when the method is invoked from the base class it is the derived class’s method that would get called. Here is the program that works similar to this situation.
using System;

namespace delegateinherit
{
  public delegate void del1();
  class mybase
  {
   public del1 d;
   public void fun()
   {
    d();
   }
  }
  class der : mybase
  {
   public der()
   {
    d = new del1 (f1);
    fun();
    d = new del1 (f2);
    fun();
   }
   public void f1()
   {
    Console.WriteLine (“Reached in f1”) ;
   }
   public void f2()
   {
    Console.WriteLine (“Reached in f2”) ;
   }
  }
  class Class1
  {
   static void Main (string[] args)
   {
    der d = new der();
   }
  }
}
Here, from the fun( ) method of the mybase class we have used the delegate object d to invoke a method. The fun( ) method is called twice from the constructor of the derived class der. Before calling fun( ) method, we have associated the delegate d, first with method f1( ) and then with f2( ). Thus, if we create an object of the der class, first the f1( ) and then f2( ) method get called. We can invoke both the methods in a single call to fun( ) as shown below.
d = new del1 ( f1 ) ;
d += new del1 ( f2 ) ;
fun( ) ;
Where Delegates are useful
In a general WinForm application, a class Form1 gets created, which is derived from the Form class. If we want to handle mouse click messages, we override the OnMouseDown( ) message handler in the Form1 class. When we click the mouse button, control reaches to the OnMouseDown( ) of the derived class i.e Form1 class because of the simple rule of inheritance. But if we click on a push button on the form, the OnMouseDown( ) of the Form1 class does not get called. Because Form1 class is not derived from the class that creates button. In such a situation, delegates prove useful. Calling methods using delegate require only that the signature of methods and that of the delegate should match. It does not require any inheritance chain to call the appropriate method. So, in .NET, all the event handlers are called through delegates, instead of relying on the inheritance chain.
Delegate at work
As said earlier, all the event handlers are called using delegates. We will now see a dummy program to explain how the Paint event handler must be getting called using a delegate.
using System;
namespace delegateatwork
{
   public delegate void PaintHandler();
   class Form
   {
  public PaintHandler Paint ;
  public void OnPaint()
  {
   Paint();
  }
   }
   class form1 : Form
   {
  public form1()
  {
   Paint += new PaintHandler (form1_Paint);
   OnPaint() ;
  }
  public void form1_Paint()
  {
   Console.WriteLine (“form1_Paint”);
  }
 }
   class Class1
   {
  static void Main (string[] args)
  {
   form1 f = new form1();
  }
   }
}
We have designed a class Form1 that contains a method called Form1_Paint( ). The Form1 class is derived from the Form class. In the Form class we have declared an object Paint of the delegate class PaintHandler. In Main( ) we have instantiated an object of the Form1 class. This results in its constructor being called. In the constructor of the Form1 class we have initialised the Paint object and encapsulated in it the Form1_Paint( ) method. Next we have called the OnPaint( ) method of the Form class (in WinForm application Paint event gets generated automatically), which in turn calls the Form1_Paint( ) method via Paint.

Read Better Linq and Entity Frameworks

http://entityframeworktutorial.net/default.aspx

Thursday, November 8, 2012

Transact-SQL (Using triggers to maintain referential integrity)


Triggers are used to maintain referential integrity, which assures that vital data in your database--such as the unique identifier for a given piece of data--remains accurate and can be used as the database changes. Referential integrity is coordinated through the use of primary and foreign keys.
The primary key is a column or combination of columns whose values uniquely identify a row. The value cannot be null and must have a unique index. A table with a primary key is eligible for joins with foreign keys in other tables. Think of the primary key table as the master table in a master-detail relationship. There can be many such master-detail groups in a database.
You can use sp_primarykey to mark a primary key. This marks the key for use with sp_helpjoins and adds it to the syskeys table.
For example, the title_id column is the primary key of titles. It uniquely identifies the books in titles and joins with title_id in titleauthorsalesdetail, and roysched. The titles table is the master table in relation to titleauthorsalesdetail, and roysched.
The "Diagram of the pubs2 database" shows how the pubs2 tables are related. The "Diagram of the pubs3 database" provides the same information for the pubs3database.
The foreign key is a column or combination of columns whose values match the primary key. A foreign key does not have to be unique. It is often in a many-to-one relationship to a primary key. Foreign key values should be copies of the primary key values. That means no value in the foreign key should exist unless the same value exists in the primary key. A foreign key may be null; if any part of a composite foreign key is null, the entire foreign key must be null. Tables with foreign keys are often called detail tables or dependent tables to the master table.
You can use sp_foreignkey to mark foreign keys in your database. This flags them for use with sp_helpjoins and other procedures that reference the syskeys table.
The title_id columns in titleauthorsalesdetail, and roysched are foreign keys; the tables are detail tables.
In most cases, you can enforce referential integrity between tables using the referential constraints described under "Specifying referential integrity constraints", because the maximum number of references allowed for a single table is 200. If a table exceeds that limit, or has special referential integrity needs, use referential integrity triggers.
Referential integrity triggers keep the values of foreign keys in line with those in primary keys. When a data modification affects a key column, triggers compare the new column values to related keys by using temporary work tables called trigger test tables. When you write your triggers, you base your comparisons on the data that is temporarily stored in the trigger test tables.

Testing data modifications against the trigger test tables

Adaptive Server uses two special tables in trigger statements: the deleted table and the inserted table. These are temporary tables used in trigger tests. When you write triggers, you can use these tables to test the effects of a data modification and to set conditions for trigger actions. You cannot directly alter the data in the trigger test tables, but you can use the tables in select statements to detect the effects of an insertupdate, or delete.
  • The deleted table stores copies of the affected rows during delete and update statements. During the execution of a delete or update statement, rows are removed from the trigger table and transferred to the deleted table. The deleted and trigger tables ordinarily have no rows in common.
  • The inserted table stores copies of the affected rows during insert and update statements. During an insert or an update, new rows are added to theinserted and trigger tables at the same time. The rows in inserted are copies of the new rows in the trigger table.
    The following trigger fragment uses the inserted table to test for changes to the titles table title_id column:
    if (select count(*) 
        from titles, inserted 
        where titles.title_id = inserted.title_id) !=
        @@rowcount 

An update is, effectively, a delete followed by an insert; the old rows are copied to the deleted table first; then the new rows are copied to the trigger table and to the inserted table. The following illustration shows the condition of the trigger test tables during an insert, a delete, and an update:
Figure 16-1: Trigger test tables during insert, delete, and update operationsrasterWhen setting trigger conditions, use the trigger test tables that are appropriate for the data modification. It is not an error to reference deleted while testing an insertor inserted while testing a delete; however, those trigger test tables will not contain any rows.
A given trigger fires only once per query. If trigger actions depend on the number of rows affected by a data modification, use tests, such as an examination of@@rowcount for multirow data modifications, and take appropriate actions.
The following trigger examples accommodate multirow data modifications where necessary. The @@rowcount variable, which stores the "number of rows affected" by the most recent data modification operation, tests for a multirow insertdelete, or update. If any other select statement precedes the test on @@rowcountwithin the trigger, use local variables to store the value for later examination. All Transact-SQL statements that do not return values reset @@rowcount to 0.

Insert trigger example

When you insert a new foreign key row, make sure the foreign key matches a primary key. The trigger should check for joins between the inserted rows (using theinserted table) and the rows in the primary key table, and then roll back any inserts of foreign keys that do not match a key in the primary key table.
The following trigger compares the title_id values from the inserted table with those from the titles table. It assumes that you are making an entry for the foreign key and that you are not inserting a null value. If the join fails, the transaction is rolled back.
create trigger forinsertrig1 
on salesdetail 
for insert 
as 
if (select count(*) 
    from titles, inserted 
    where titles.title_id = inserted.title_id) !=
    @@rowcount 
/* Cancel the insert and print a message.*/
  begin
    rollback transaction 
    print "No, the title_id does not exist in
    titles." 
  end  
/* Otherwise, allow it. */
else
  print "Added! All title_id's exist in titles."

@@rowcount refers to the number of rows added to the salesdetail table. This is also the number of rows added to the inserted table. The trigger joins titles andinserted to determine whether all the title_ids added to salesdetail exist in the titles table. If the number of joined rows, which is determined by the select count(*)query, differs from @@rowcount, then one or more of the inserts is incorrect, and the transaction is canceled.
This trigger prints one message if the insert is rolled back and another if it is accepted. To test for the first condition, try this insert statement:
insert salesdetail 
values ("7066", "234517", "TC9999", 70, 45)

To test for the second condition, enter:
insert salesdetail 
values ("7896", "234518", "TC3218", 75, 80) 

Delete trigger examples

When you delete a primary key row, delete corresponding foreign key rows in dependent tables. This preserves referential integrity by ensuring that detail rows are removed when their master row is deleted. If you do not delete the corresponding rows in the dependent tables, you may end up with a database with detail rows that cannot be retrieved or identified. To properly delete the dependent foreign key rows, use a trigger that performs a cascading delete.
Cascading delete exampleWhen a delete statement on titles is executed, one or more rows leave the titles table and are added to deleted. A trigger can check the dependent tables--titleauthorsalesdetail, and roysched--to see if they have any rows with a title_id that matches the title_ids removed from titles and is now stored in the deletedtable. If the trigger finds any such rows, it removes them.
create trigger delcascadetrig 
on titles 
for delete 
as 
delete titleauthor 
from titleauthor, deleted 
where titleauthor.title_id = deleted.title_id 
/* Remove titleauthor rows that match deleted
** (titles) rows.*/
delete salesdetail 
from salesdetail, deleted 
where salesdetail.title_id = deleted.title_id 
/* Remove salesdetail rows that match deleted
** (titles) rows.*/ 
delete roysched 
from roysched, deleted 
where roysched.title_id = deleted.title_id 
/* Remove roysched rows that match deleted
** (titles) rows.*/

Restricted delete examplesIn practice, you may want to keep some of the detail rows, either for historical purposes (to check how many sales were made on discontinued titles while they were active) or because transactions on the detail rows are not yet complete. A well-written trigger should take these factors into consideration.

Preventing primary key deletions

The deltitle trigger supplied with pubs2 prevents the deletion of a primary key if there are any detail rows for that key in the salesdetail table. This trigger preserves the ability to retrieve rows from salesdetail:
create trigger deltitle 
on titles 
for delete 
as 
if (select count(*) 
    from deleted, salesdetail 
    where salesdetail.title_id = 
    deleted.title_id) > 0 
  begin 
    rollback transaction 
    print "You cannot delete a title with sales."
  end

In this trigger, the row or rows deleted from titles are tested by being joined with the salesdetail table. If a join is found, the transaction is canceled.
Similarly, the following restricted delete prevents deletes if the primary table, titles, has dependent children in titleauthor. Instead of counting the rows from deletedand titleauthor, it checks to see if title_id was deleted. This method is more efficient for performance reasons because it checks for the existence of a particular row rather than going through the entire table and counting all the rows.

Recording errors that occur

The next example uses raiserror for error message 35003. raiserror sets a system flag to record that the error occurred. Before trying this example, add error message 35003 to the sysusermessages system table:
sp_addmessage 35003, "restrict_dtrig - delete failed: row exists in titleauthor for this title_id."

The trigger is:
create trigger restrict_dtrig
on titles
for delete as
if exists (select * from titleauthor, deleted where 
          titleauthor.title_id = deleted.title_id)
   begin
           rollback transaction
           raiserror 35003
           return
   end

To test this trigger, try this delete statement:
delete titles
where title_id = "PS2091"

Update trigger examples

The following example cascades an update from the primary table titles to the dependent tables titleauthor and roysched.
create trigger cascade_utrig
on titles
for update as
if update(title_id)
begin
     update titleauthor
           set title_id = inserted.title_id
           from titleauthor, deleted, inserted
           where deleted.title_id = titleauthor.title_id
     update roysched
           set title_id = inserted.title_id
           from roysched, deleted, inserted
           where deleted.title_id = roysched.title_id
     update salesdetail
           set title_id = inserted.title_id
           from salesdetail, deleted, inserted
           where deleted.title_id = salesdetail.title_id
end

To test this trigger, suppose that the book Secrets of Silicon Valley was reclassified to a psychology book from popular_comp. The following query updates thetitle_id PC8888 to PS8888 in titleauthorroysched, and titles.
update titles
set title_id = "PS8888"
where title_id = "PC8888"

Restricted update triggersA primary key is the unique identifier for its row and for foreign key rows in other tables. Generally, you should not allow updates to primary keys. An attempt to update a primary key should be taken very seriously. In this case, you need to protect referential integrity by rolling back the update unless specified conditions are met.
Sybase suggests that you prohibit any editing changes to a primary key, for example by revoking all permissions on that column. However, if you want to prohibit updates only under certain circumstances, use a trigger.

Restricted update trigger using date functions

The following trigger prevents updates to titles.title_id on the weekend. The if update clause in stopupdatetrig allows you to focus on a particular column,titles.title_id. Modifications to the data in that column cause the trigger to go into action. Changes to the data in other columns do not. When this trigger detects an update that violates the trigger conditions, it cancels the update and prints a message. If you would like to test this one, substitute the current day of the week for "Saturday" or "Sunday".
create trigger stopupdatetrig 
on titles 
for update 
as 
/* If an attempt is made to change titles.title_id
** on Saturday or Sunday, cancel the update. */
if update (title_id) 
    and datename(dw, getdate()) 
    in ("Saturday", "Sunday") 
  begin 
    rollback transaction 
    print "We do not allow changes to "
    print "primary keys on the weekend." 
  end

Restricted update triggers with multiple actions

You can specify multiple trigger actions on more than one column using if update. The following example modifies stopupdatetrig to include additional trigger actions for updates to titles.price or titles.advance. In addition to preventing updates to the primary key on weekends, it prevents updates to the price or advance of a title, unless the total revenue amount for that title surpasses its advance amount. You can use the same trigger name because the modified trigger replaces the old trigger when you create it again.
create trigger stopupdatetrig 
on titles 
for update 
as 
if update (title_id) 
  and datename(dw, getdate()) 
  in ("Saturday", "Sunday") 
  begin 
    rollback transaction 
    print "We do not allow changes to"
    print "primary keys on the weekend!" 
  end 
if update (price) or update (advance)
  if exists (select * from inserted
    where (inserted.price * inserted.total_sales)
    < inserted.advance)
    begin
      rollback transaction
      print "We do not allow changes to price or"
      print "advance for a title until its total"
      print "revenue exceeds its latest advance."
    end

The next example, created on titles, prevents update if any of the following conditions is true:
  • The user tries to change a value in the primary key title_id in titles
  • The dependent key pub_id is not found in publishers
  • The target column does not exist or is null

Before you run this example make sure the following error messages exist in sysusermessages:
sp_addmessage 35004, "titles_utrg - Update Failed: update of primary keys %1! is not allowed."
sp_addmessage 35005, "titles_utrg - Update Failed: %1! not found in authors."

The trigger is as follows:
create trigger title_utrg
on titles
for update as
begin
     declare @num_updated int,
             @col1_var varchar(20),
             @col2_var varchar(20)
/* Determine how many rows were updated. */
select @num_updated = @@rowcount
    if @num_updated = 0
    return
/* Ensure that title_id in titles is not changed. */
if update(title_id)
    begin
       rollback transaction
     select @col1_var = title_id from inserted
     raiserror 35004 , @col1_var
     return
   end
 /* Make sure dependencies to the publishers table are accounted for. */
 if update(pub_id)
   begin
     if (select count(*) from inserted, publishers
         where inserted.pub_id = publishers.pub_id
         and inserted.pub_id is not null) != @num_updated
     begin
         rollback transaction
         select @col1_var = pub_id from inserted
         raiserror 35005, @col1_var
         return
     end
   end
/* If the column is null, raise error 24004 and rollback the
** trigger. If the column is not null, update the roysched table
** restricting the update. */
   if update(price)
     begin
         if exists (select count(*) from inserted 
         where price = null)
     begin
         rollback trigger with
         raiserror 24004 "Update failed : Price cannot be null. "
     end
     else
     begin
         update roysched 
         set lorange = 0,
         hirange = price * 1000
         from inserted
         where roysched.title_id =  inserted.title_id
     end
    end 
end

To test for the first error message, 35004, enter:
update titles
set title_id = "BU7777"
where title_id = "BU2075"

To test for the second error message, 35005:
update titles
set pub_id = "7777"
where pub_id = "0877"

To test for the third error, which generates message 24004:
update titles
set price = 10.00
where title_id = "PC8888"

This query fails because the price column in titles is null. If it were not null, it would have updated the price for title PC8888 and performed the necessary recalculations for the roysched table. Error 24004 is not in sysusermessages but it is valid in this case. It demonstrates the "rollback trigger with raiserror" section of the code.
Updating a foreign keyA change or an update to a foreign key by itself is probably an error. A foreign key is just a copy of the primary key. Never design the two to be independent. If you want to allow updates of a foreign key, you should protect integrity by creating a trigger that checks updates against the master table and rolls them back if they do not match the primary key.
In the following example, the trigger tests for two possible sources of failure: either the title_id is not in the salesdetail table or it is not in the titles table.
This example uses nested if...else statements. The first if statement is true when the value in the where clause of the update statement does not match a value insalesdetail, that is, the inserted table will not contain any rows, and the select returns a null value. If this test is passed, the next if statement ascertains whether the new row or rows in the inserted table join with any title_id in the titles table. If any row does not join, the transaction is rolled back, and an error message is printed. If the join succeeds, a different message is printed.
create trigger forupdatetrig 
on salesdetail 
for update 
as 
declare @row int 
/* Save value of rowcount. */
select @row = @@rowcount  
if update (title_id) 
  begin 
    if (select distinct inserted.title_id 
        from inserted) is null 
      begin 
        rollback transaction 
        print "No, the old title_id must be in"
        print "salesdetail." 
      end 
    else 
      if (select count(*) 
          from titles, inserted 
          where titles.title_id = 
          inserted.title_id) != @row 
        begin 
          rollback transaction 
          print "No, the new title_id is not in"
          print "titles." 
        end 
      else
        print "salesdetail table updated" 
  end