Archive for May, 2005

transaction gotcha

Posted: May 28, 2005 in Uncategorized

In my last post here yesterday i d posted on webservice automatic transaction and how it got failed the ACID test.Today i checked if the same is gtiign failed with com+ or not and luckily com+ passed the test.yeah the same code passed the ACID.

That is if there is an error the first insert to first is getting rolled back.Thus it looks like a lotcha with webservice automatic transaction.

tomorrow i ll post the result of the same uysing sp_addlinkserver and running 2 different server in same transaction.

note:-create the dll and regd in com+ and for the componet defined transaction aas requirenew

 

{

SqlConnection con;

SqlCommand cmd;

string strcon="constr1:first connstr to server1";

using (con=new SqlConnection(strcon))

{

con.Open();

cmd=

new SqlCommand();

cmd.CommandType=CommandType.StoredProcedure;

cmd.CommandText="proc_tran";

cmd.Connection =con;

cmd.ExecuteNonQuery();

}

//call webservice 2

//spawnservice.Execchild svc=new spawnservice.Execchild();

//svc.execute();

string strcon1="second constr to server2";

SqlConnection con1;

SqlCommand cmd1;

using (con1=new SqlConnection(strcon1))

{

con1.Open();

cmd1=

new SqlCommand();

cmd1.CommandType=CommandType.StoredProcedure;

cmd1.CommandText="child_proc";

cmd1.Connection =con1;

cmd1.ExecuteNonQuery();

}

return "success";

}

catch(Exception ex)

{

return ex.Message.ToString();

}

}

here goes the calling code:-

try

{

testreference.reftest test=

new reftest();

string s=test.enlisttran();

MessageBox.Show(s);

}

catch(Exception ex)

{

MessageBox.Show(ex.Message.ToString());

}

use sqlserver and u still get the same rolled back if error happened while executing the second command:-

EXEC sp_addlinkedserver  

   @server=’addlnk_remsvr’,

   @srvproduct=”,

   @provider=’SQLNCLI’,

   @datasrc=’remsvrsystemname’

 

begin tran

 –SET XACT_ABORT ON

insert emp values(21,’shreeman21′,getdate(),1111,50)—local svr

if @@error<>0

rollback tran

insert addlnk_remsvr.test.dbo.tempemp values(1,’shreeman’)–remote svr

if @@error<>0

rollback tran

–SET  XACT_ABORT OFF

commit tran

 

Luckily or you can say unlucky i got the issue  (due to the sp2 pack and my remote server setting changed) .

if you look at the behavior it is not recommenede to use a distributed transaction using autotransaction features of webservices but i wonder why the same was  not raised before may be due to the fact that nobody might ve used may be i m wrong..if somebody can point i would be more happy.

 

Note:In connectionstring i tried enlist=true as well but with out luck.

 

As a final note few annoying msdtc errors i am fighting with and have gone thru few support articles:-

 

 

New transaction cannot enlist in the specified transaction coordinator.

 

The operation could not be performed because the OLE DB provider ‘SQLNCLI’ for linked server ‘addlnk_remsvr’ was unable to begin a distributed transaction.

The operation could not be performed because the OLE DB provider ‘SQLOLEDB’ was unable to begin a distributed transaction.

 

The transaction manager has disabled its support for remote/network transactions

 

http://support.microsoft.com/default.aspx?scid=kb;EN-US;817064

http://support.microsoft.com/?kbid=873160

 

Note:-The purpose of this note is to put a notice on the exceptional behavior of msdtc with webservice autotransaction.

Advertisements

There is a serious gotch in webservice transaction that is with distributed transaction the webmethod transaction not followed the correct rollback and it ddin’t follow ACID.

Below is such a gotcha i found :-I am calling 2 diiferent server with 2 connections and inserting values into 2 different tables in a transaction(server1–>table1 and server2–>table2).

while i am trying the same even though my second method is getting failed  due to

"The transaction manager has disabled its support for remote/network transactions"

the rollback didn’t happens for the first one.

thus even though msdtc failed for whatever reason since both are in a transaction the first should be rolled back.

Today i was working on one requirement to accomplish the following :-I need to sync up data in two different server at run time.

that is i need to create a transaction such that if i insert values in to table1 in server1 and table2 in server2 .I had 3 option

1.to use transaction in backend and use addlinkserver and put both inside the transaction

2.craete 2 diff component in com+(ES) and put them in single transeaction

3.mark the webmethod in trasaction.

i followed the 3rd option but to mywonder thouggh the second method is failing with msdtc error the transaction is not getting rolled back.

here goes my code:-

using

parentwebservice.spawnservice;using System.EnterpriseServices;

[WebMethod(TransactionOption=TransactionOption.RequiresNew)]

            public string transact()

            {

                  try

                  {

                 

                        string strcon="connstr to first server ";

                        using (con=new SqlConnection(strcon))

                        {

                              con.Open();            

                              cmd=new SqlCommand();

                              cmd.CommandType=CommandType.StoredProcedure;

                              cmd.CommandText="proc_tran";

                              cmd.Connection =con;

                              cmd.ExecuteNonQuery();

                                                                                               

                        }

                        //call webservice 2

                        //spawnservice.Execchild svc=new spawnservice.Execchild();

                        //svc.execute();

                        //since the webservice call  gives me error 401 I directly using the

                        //second con here to carry my task

                        string strcon1="connstr to second server";

                        SqlConnection con1;

                        SqlCommand cmd1;

                        using (con1=new SqlConnection(strcon1))

                        {

                              con1.Open();                 

                              cmd1=new SqlCommand();

                              cmd1.CommandType=CommandType.StoredProcedure;

                              cmd1.CommandText="child_proc";

                              cmd1.Connection =con;

                              cmd1.ExecuteNonQuery();                  

                                                                       

                        }

 

                        return "success";

                  }

                  catch(Exception ex)

                  {

                        return ex.Message.ToString();

                  }

                 

            }

 

The next post i post with the msdtc experience with com+ and .net and later i discuss how in backend we use the same.

 

 

 

I saw post in .netindiagroup and the answer i thought might be interested for some.so here goes the thread:-

The question:-

I want to get the path of the Assemblies referenced by an assembly. I’m using the following code. I’m able to get the Referenced assemblies name, But
i’m not able to get the path in which the assemblies reside(physical path). Its giving me the path of mscorlib.dll’s path, but not the path of the assemblies which i created.
 
Assembly  assembly = Assembly.LoadFrom(Path);
AssemblyName[] a =  assembly.GetReferencedAssemblies();
 
foreach(AssemblyName an in a)
{
   MessageBox.Show(an.name);  


}      
 
The answer
This is a interesting behavior you mentionmed about .Before propviding the solution let me clarify something regd ur post.First of all u r tryign to get the referenced assemblies which resides at path correct?
 
Now coming to the solution the .net reflection will use only those assemblies which you have referenced & used that is try to create an instance of the referenced assemblies access some method in constructor and its return you the name .This behavior is by design that is .net compiuler will check the config at compile time and since you didn’t have accessed the method or created the instanmce it was not showing u the asembly you are pointing at.(this is by design)
 
hope this helps .you can use any of the code u ve written or For current assembly you can use

AssemblyName[] assmbl=Assembly.GetExecutingAssembly().GetReferencedAssemblies();

foreach(AssemblyName assmblnm in assmbl )

{

MessageBox.Show(assmblnm.Name.ToString());

}

 

Another interesting point is to get teh location(physical ) of the referenced assemblies:– try assmblnm.codebase and you get ""(blank).this is due to the fact the since the same is not loaded yet you can’t get a path thus the remedy is

AssemblyName[] assmbl=Assembly.GetExecutingAssembly().GetReferencedAssemblies();

foreach(AssemblyName assmblnm in assmbl )

{Assembly asm=Assembly.load(assmblnm);

MessageBox.Show(asm.Location); //or use asm.CodeBase

}

or

For dynamically loaded assembly

string path=@"D:\vsnet20039052005\testreference\bin\Debug\testreference.dll";

Assembly assembly = Assembly.LoadFrom(path);

AssemblyName[] a = assembly.GetReferencedAssemblies();

foreach(AssemblyName an in a )

{

Assembly asm=Assembly.Load(an);

string s=asm.Location;//or use asm.CodeBase;

MessageBox.Show(s);

}

I wonder what all we can’t do while using reflection.emit for codegen and i didn’t found much documentation regd the same and the features were not very well understood.upon my request i found yiru had posted few very good points on the reflectionm.emit limitation.here is the link http://blogs.msdn.com/yirutang/archive/2005/05/19/420134.aspx

thanks yiru.

 

While replying to a post on .netindiagroup I found the codedom has pretty strange behavior and quite a few limitation are incorporate with the same.First the question asked was why it simply can’t create a signature of method like

public rettype funcname() {}

even though u passed the members attribute as public it generates

public virtual rettype funcname(){}

Further if u need 2 attributes let say protected internal …

here are other limitation for codedom:-(From BCL team BLOG)

note :we can use the syntax property.attribute=memberattribute.attribute1 | memberattributes.attrib2 as well for defining 2 attributes for the method sig.

Here is the answer to generate a public signature

property.Attributes = (property.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public ;

(thanks to vinay bhusan gattam redyy and Ms peoples liek the Yiru,joel pobar,KitG to correct me).

sample codedom to create a class on the fly:-

//generate a strem to write with

Stream cFile = File.Open(@"C:\Documents and Settings\shdash\Desktop\sample.cs", FileMode.Create);

StreamWriter sw = new StreamWriter(cFile);

//generate a csharp provider and a codegenerator and codesnippetcompileunit

CSharpCodeProvider provider=new CSharpCodeProvider();

ICodeGenerator codegen=provider.CreateGenerator(sw);

CodeGeneratorOptions codegenopt=new CodeGeneratorOptions();

CodeSnippetCompileUnit snippetunit=new CodeSnippetCompileUnit("using System");

codegen.GenerateCodeFromCompileUnit(snippetunit,sw,codegenopt);

//craete name spaces and class and teh class to namespace

CodeNamespace namespc=new CodeNamespace("samplecodedomnamespace");

CodeTypeDeclaration clsdec=new CodeTypeDeclaration();

clsdec.Name="clscodedom";

clsdec.IsClass =true;

clsdec.TypeAttributes=TypeAttributes.Public;

//add a constructor to the class

namespc.Types.Add(clsdec);

CodeConstructor constr=new CodeConstructor();

constr.Attributes=MemberAttributes.Public;

clsdec.Members.Add(constr);

//add a public member field to the class

CodeMemberField clsmem=new CodeMemberField();

clsmem.Name="classmem";

clsmem.Attributes=MemberAttributes.Public;

clsmem.Type =new CodeTypeReference("System.String");

clsdec.Members.Add (clsmem);

//add a properety to the class

CodeMemberProperty property = new CodeMemberProperty();

property.Name = "dynpop";

property.Type = new CodeTypeReference("System.String");

property.Attributes=MemberAttributes.Public;

property.GetStatements.Add( new CodeMethodReturnStatement( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "dynpop") ) );

property.SetStatements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(),"dynpop"), new CodePropertySetValueReferenceExpression()));

clsdec.Members.Add(property);

//generate the code

codegen.GenerateCodeFromNamespace(namespc,sw,codegenopt);

sw.Close();

cFile.Close();