1/18/11

Covariance And Contravariance In Generics

C# 4.0 (and .NET 4.0) introduced covariance and contravariance to generic interfaces and delegates. But what is this variance thing?

According to Wikipedia, in multilinear algebra and tensor analysis, covariance and contravariance describe how the quantitative description of certain geometrical or physical entities changes when passing from one coordinate system to another.(*)

But what does this have to do with C# or .NET?

In type theory, a the type T is greater (>) than type S if S is a subtype (derives from) T, which means that there is a quantitative description for types in a type hierarchy.

So, how does covariance and contravariance apply to C# (and .NET) generic types?

In C# (and .NET), variance is a relation between a generic type definition and a particular generic type parameter.

Given two types Base and Derived, such that:

* There is a reference (or identity) conversion between Base and Derived
* Base ≥ Derived

A generic type definition Generic is:

* covariant in T if the ordering of the constructed types follows the ordering of the generic type parameters: Generic ≥ Generic.
* contravariant in T if the ordering of the constructed types is reversed from the ordering of the generic type parameters: Generic ≤ Generic.
* invariant in T if neither of the above apply.

If this definition is applied to arrays, we can see that arrays have always been covariant in relation to the type of the elements because this is valid code:

object[] objectArray = new string[] { "string 1", "string 2" };
objectArray[0] = "string 3";
objectArray[1] = new object();

However, when we try to run this code, the second assignment will throw an ArrayTypeMismatchException. Although the compiler was fooled into thinking this was valid code because an object is being assigned to an element of an array of object, at run time, there is always a type check to guarantee that the runtime type of the definition of the elements of the array is greater or equal to the instance being assigned to the element. In the above example, because the runtime type of the array is array of string, the first assignment of array elements is valid because string ≥ string and the second is invalid because string ≤ object.

This leads to the conclusion that, although arrays have always been covariant in relation to the type of the elements, they are not safely covariant – code that compiles is not guaranteed to run without errors.

In C#, variance is enforced in the declaration of the type and not determined by the usage of each the generic type parameter.

Covariance in relation to a particular generic type parameter is enforced, is using the out generic modifier:

public interface IEnumerable
{
IEnumerator GetEnumerator();
}

public interface IEnumerator
{
T Current { get; }
bool MoveNext();
}

Notice the convenient use the pre-existing out keyword. Besides the benefit of not having to remember a new hypothetic covariant keyword, out is easier to remember because it defines that the generic type parameter can only appear in output positions — read-only properties and method return values.

In a similar way, the way contravariance is enforced in relation a particular generic type parameter, is using the in generic modifier:

public interface IComparer
{
int Compare(T x, T y);
}

Once again, the use of the pre-existing in keyword makes it easier to remember that the generic type parameter can only be used in input positions — write-only properties and method non ref and non out parameters.

A generic type parameter that is not marked covariant (out) or contravariant (in) is invariant.

Because covariance and contravariance applies to the relation between a generic type definition and a particular generic type parameter, a generic type definition can be both covariant, contravariant and invariant depending on the generic type parameter.

public delegate TResult Func(T arg);

In the above delegate definition, Func is contravariant in T and convariant in TResult.

All the types in the .NET Framework where variance could be applied to its generic type parameters have been modified to take advantage of this new feature.

In summary, the rules for variance in C# (and .NET) are:

* Variance in relation to generic type parameters is restricted to generic interface and generic delegate type definitions.
* A generic interface or generic delegate type definition can be covariant, contravariant or invariant in relation to different generic type parameters.
* Variance applies only to reference types: a IEnumerable is not an IEnumerable.
* Variance does not apply to delegate combination. That is, given two delegates of types Action and Action, you cannot combine the second delegate with the first although the result would be type safe. Variance allows the second delegate to be assigned to a variable of type Action, but delegates can combine only if their types match exactly.


Enable Transactions in WCF

Introduction and Goal

In this article, we will try to understand how we can implement transactions in WCF service. So we will create two WCF services which do database transactions and then unite them in one transaction. We will first understand the 6 important steps to enable transactions in WCF services. At the end of the article, we will try to force an error and see how the transaction is rolled back after the error.

Now a days, I am distributing my 400 questions and answers ebook which covers major .NET related topics like WCF, WPF, WWF, Ajax, Core .NET, SQL Server, Architecture and a lot more. I am sure you will enjoy this ebook. You can download my Ebook from here.

Step 1: Create Two WCF Services

The first step is to create two WCF service projects which will participate in one transaction. In both of these WCF services, we will do database transactions and we will try to understand how a WCF transaction unifies them. We have also created a web application with name WCFTransactions which will consume both the services in one transaction scope.

Step 2: Attribute Interface Methods with TransactionFlow

In both the WCF services, we will create a method called UpdateData which will insert into the database. So the first thing is to create the interface class with ServiceContract attribute and the method UpdateData with OperationContract attribute. In order to enable transaction in UpdateData method, we need to attribute it with TransactionFlow and we have specified that transactions are allowed for this method using TransactionFlowOption.Allowed enum.
Collapse

[ServiceContract]
public interface IService1
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed)]
void UpdateData();
}

Step 3: Attribute the Implementation with TransactionScopeRequired

The 3rd step is to attribute the implementation of the WCF services with TransactionScopeRequired as true. Below is the code snippet which has a simple database inserting function, i.e. UpdateData which is attributed by TransactionScopeRequired attribute.
Collapse

[OperationBehavior(TransactionScopeRequired = true)]
public void UpdateData()
{
SqlConnection objConnection = new SqlConnection(strConnection);
objConnection.Open();
SqlCommand objCommand = new SqlCommand("insert into Customer
(CustomerName,CustomerCode) values('sss','sss')",objConnection);
objCommand.ExecuteNonQuery();
objConnection.Close();
}

Step 4: Enable Transaction Flow using WCF Service Config File

We also need to enable transactions for wsHttpBinding by setting the transactionFlow attribute to true.
Collapse


The transaction enabled binding we need to attach with the end point through which our WCF service is exposed.
Collapse


Step 5: Call the 2 Services in One Transaction

Now that we are done with enabling our server side transaction, it’s time to call the above 2 services in 1 transaction. We need to use the TransactionScope object to group the above 2 WCF services in one transaction. To commit all the WCF transactions, we call the Complete method of the Transactionscope object. To rollback, we need to call the Dispose method.
Collapse

using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))
{
try
{

// Call your webservice transactions here
ts.Complete();
}
catch (Exception ex)
{
ts.Dispose();
}
}

Below is the complete code snippet in which we have grouped both the WCF transactions in one scope as shown below:
Collapse

using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))
{
try
{
ServiceReference1.Service1Client obj = new ServiceReference1.Service1Client();
obj.UpdateData();
ServiceReference2.Service1Client obj1 = new ServiceReference2.Service1Client();
obj1.UpdateData();
ts.Complete();
}
catch (Exception ex)
{
ts.Dispose();
}
}

Step 6: Test If Your Transaction Works

It’s time to test if the transactions really work. We are calling two services, both of which are doing an insert. After the first WCF service call, we are forcing an exception. In other words, the data insert of the first WCF service should revert back. If you check the database records, you will see no records are inserted by the WCF service.
Click to enlarge

Welcome