Thursday, April 16, 2009

Calling Alfresco Web Services from C#

Hi,
I am starting this topic to share my experience of calling Alfresco Web Services from .Net, the problems which you may face and the solutions.
I first started with scratch. I created a C# project and added a Web reference to AuthenticationService (authentication-service.wsdl) , and VersioningService (VersioningService.wsdl).
Then I wrote following lines of code


WindowsApplication1.AuthenticationService.AuthenticationService asvc = new WindowsApplication1.AuthenticationService.AuthenticationService();
AuthenticationResult ar = asvc.startSession("admin", "admin");

It worked fine. Examining ar showed the token returned by Alfresco.
I then wrote the following lines

VersioningService vs = new VersioningService();
string repID = ("xyz");
string docID = ("abc");
string docRefId = docID;
bool b = vs.checkOut(repID, ref docRefId);

I was expecting an exception to be generated for giving invalid parameters, but the exception generated was about target service is null. It seems VersioningService is not exposed at URL/alfresco/api/VersioningService.
I then tried to add reference to RepositoryService (repository-service.wsdl ). Visual Studio reported a problem:
The document was understood, but it could not be processed. The WSDL document contains links that could not be resolved.
The solution was to remove the nillable attributes in cml.xsd files. After doing that, it was able to add the reference. I then wrote the following lines

RepositoryService rs = new RepositoryService();
rs.Credentials = asvc.Credentials;
Store[] stores = rs.getStores();


The exception which got generated was about Unauthorization. It was simple to figure out the cause, I was not passing in the security token, it was hard to figure out how to pass that. I was stuck.
I then googled and found out that there is demo project called dotNet2 for calling Alfresco APIs from .Net. I downloaded the same and tried to build it. It turned out that there was a missing reference to Microsoft.Web.Services3 assembly. Solution was to install MS WSE 3. So well and good.
I tried building the solution and there was a problem:

Error 1 The best overloaded method match for 'WebServiceFactory.addSecurityHeader(Microsoft.Web.Services3.WebServicesClientProtocol)' has some invalid arguments
Error 2 Argument '1': cannot convert from 'Alfresco.RepositoryWebService.RepositoryService' to 'Microsoft.Web.Services3.WebServicesClientProtocol'

I tried google to find out the solution for this, and I got one but it didn't work. A close observation revealed that addSecurityHeader was expecting Microsoft.Web.Services3.WebServicesClientProtocol object the service objects which we were passing were derived from SoapHttpClientProtocol class. I found that Microsoft.Web.Services3.WebServicesClientProtocol itself derives from SoapHttpClientProtocol, so I just let the web services class derive from Microsoft.Web.Services3.WebServicesClientProtocol and it worked!!
Then I tried to call getStores() method of RepositoryWebService and I again encountered exception. There was some problem in class instantiation from the returned response. Close observation again revealed the problem. The StoreEnum of RepositoryWebService was defined like this

public enum StoreEnum {
///
workspace,
///
versionStore,
///
user,
///
search,
///
http,
///
system,
}

I extended this enum to look like this

public enum StoreEnum {
///
workspace,
///
versionStore,
///
user,
///
search,
///
http,
///
system,
archive,
avm,
}

And after doing this, the call succeeded
Now my task was to test checking out of a selected file and getting a previous version of a selected file. After many hit and trials, I got it working and here is the reference code (I added private AuthoringService authoringService; to Browse class)
This is the code for Checking Out a selected file:
private void CheckOut_Click(object sender, EventArgs e)
{
ListViewItem item = listViewBrowse.SelectedItems[0];
if (item != null)
{
ResultSetRowNode node = item.Tag as ResultSetRowNode;
if (node != null)
{
if (node.type.Contains("folder") == false)
{
// Create the reference for the node selected
Alfresco.AuthoringWebService.Store spacesStore2 = new Alfresco.AuthoringWebService.Store();
spacesStore2.scheme = Alfresco.AuthoringWebService.StoreEnum.workspace;
spacesStore2.address = "SpacesStore";
Alfresco.AuthoringWebService.Reference reference = new Alfresco.AuthoringWebService.Reference();
reference.store = spacesStore2;
reference.uuid = node.id;
// Lets try to check out
Alfresco.AuthoringWebService.Predicate predicate = new Alfresco.AuthoringWebService.Predicate();
predicate.Items = new Object[] { reference };
Alfresco.AuthoringWebService.ParentReference pr = new Alfresco.AuthoringWebService.ParentReference();
pr.store = spacesStore2; ;
pr.uuid = this.currentReference.uuid;
pr.associationType = Constants.ASSOC_CONTAINS;
pr.childName = Constants.createQNameString(Constants.NAMESPACE_CONTENT_MODEL,item.Text);
this.authoringService.checkout(predicate, pr);
}
else
{
// show message that a folder has been selected
}
}
}
}


And this is the code for getting the original version of a file
private void GetOriginalVersion_Click(object sender, EventArgs e)
{
Alfresco.RepositoryWebService.Store[] stores = this.repoService.getStores();
Alfresco.RepositoryWebService.Store vStore = stores[3]; // from data,I found out that this is for Versioned store
ListViewItem item = listViewBrowse.SelectedItems[0];
if (item != null)
{
ResultSetRowNode node = item.Tag as ResultSetRowNode;
if (node != null)
{
if (node.type.Contains("folder") == false)
{
// Create the reference for the node selected
Alfresco.AuthoringWebService.Store spacesStore2 = new Alfresco.AuthoringWebService.Store();
spacesStore2.scheme = Alfresco.AuthoringWebService.StoreEnum.workspace;
spacesStore2.address = "SpacesStore";
Alfresco.AuthoringWebService.Reference reference = new Alfresco.AuthoringWebService.Reference();
reference.store = spacesStore2;
reference.uuid = node.id;
VersionHistory VH = this.authoringService.getVersionHistory(reference);
int i = 0;
char[] temp = new char[1];
temp[0] = '0';
string versions = new string(temp);
Alfresco.AuthoringWebService.Version first;
foreach (Alfresco.AuthoringWebService.Version version inVH.versions)
{
if (i == 0)
first = version;
versions += version.label + (";") + version.id.uuid +(";");
}
{
// Create the reference for the node selected
Alfresco.ContentWebService.Store spacesStore3 = new Alfresco.ContentWebService.Store();
spacesStore3.scheme = Alfresco.ContentWebService.StoreEnum.versionStore;
spacesStore3.address = vStore.address;
Alfresco.ContentWebService.Reference reference1 = newAlfresco.ContentWebService.Reference();
reference1.store = spacesStore3;
reference1.uuid = VH.versions[VH.versions.GetUpperBound(0)].id.uuid;
// Lets try and get the content
Alfresco.ContentWebService.Predicate predicate = newAlfresco.ContentWebService.Predicate();
predicate.Items = new Object[] { reference1 };
Content[] contents = this.contentService.read(predicate,"{http://www.alfresco.org/model/content/1.0}content");
Content content = contents[0];
if (content.url != null && content.url.Length != 0)
{
string url = content.url + "?ticket=" + AuthenticationUtils.Ticket;
webBrowser.Url = new Uri(url);
}
}
}
else
{
// show message that a folder has been selected
}
}
}
}

Here is the link for the Holy Grail dotNet2 Demo project
http://forge.alfresco.com/frs/download.php/80/alfresco-dotNet-0.2Beta.zip

And here is the the project which will allow you to do a Check Out and Get Latest version. it is an extended version of dotNet2 Demo project. You should be able to head start with it. Change the Alfresco Server location as per your requirement.

http://cid-f49e53911f11f3b1.skydrive.live.com/self.aspx/.Public/Technical/Alfresco/alfresco-dotNet-0.2Beta-Modified.zip

Thanks,

23 comments:

  1. Thanks for the great post and for taking the time to share your .NET expertise with the community.

    Nancy Garrity
    Alfresco Community Manager

    ReplyDelete
  2. Thanks Nancy. I have updated the original post to include the link to modified dotNet2 project as well.

    ReplyDelete
  3. This is great, thank you very much.

    ReplyDelete
  4. Good Job,
    Btw, I have one error with the AccessControlWebService. The error is
    " The custom tool 'MSDiscoCodeGenerator' failed. Unable to import binding 'AccessControlServiceSoapBinding' from namespace 'http://www.alfresco.org/ws/service/accesscontrol/1.0'

    Any idea?

    ReplyDelete
  5. Hi Rishi
    Do you have any code to upload files/documents from C# code,directly to alfresco.

    Regards,
    Anu

    ReplyDelete
  6. It sure gave me a starting point. thanks

    ReplyDelete
  7. Hi,
    I am using dotNet2 sample, I want to get the alfresco content item properties in that sample, is it possible? If possible, how? please help with code.

    ReplyDelete
  8. Hello,

    Im using vs2005, alfresco community 3.2 and your modified source files downloaded from this site.

    Im getting the error
    "cannot convert from 'Alfresco.RepositoryWebService.RepositoryService' to 'Microsoft.Web.Services3.WebServicesClientProtocol'"

    You said:

    "I found that Microsoft.Web.Services3.WebServicesClientProtocol itself derives from SoapHttpClientProtocol, so I just let the web services class derive from Microsoft.Web.Services3.WebServicesClientProtocol and it worked!!"

    What this means exactly? Shouldnt this change be already made in your code?

    Can you explain what you made?

    thank you!

    ReplyDelete
  9. any one knows how to create a new space under the selected space via c# ???

    ReplyDelete
  10. Hi, are u able to call alfresco web service (Repository) from WCF?
    Thank's

    ReplyDelete
  11. too bad this is not working....this is exactly what I was looking for, but when trying to biuld the project there is 16 errors, even after adding the web reference still not able to biuld

    ReplyDelete
  12. To solve the addSecurityHeader problem, check this explanation: http://forums.alfresco.com/en/viewtopic.php?f=27&t=5218&start=0#p60277

    Worked for me :)

    ReplyDelete
  13. piece of shit...none knows how to make that works!

    ReplyDelete
  14. The reference you have to change (it's not been changed in the code) is in all the Reference.cs files, one for each Web Service Reference. These are normally hidden so click on show all files in your solution explorer first.

    ReplyDelete
  15. Lets make it clear, for example: change the interface of public partial class ContentService from: System.Web.Services.Protocols.SoapHttpClientProtocol
    into: Microsoft.Web.Services3.WebServicesClientProtocol

    ReplyDelete
  16. How to get the metadata from a content for ex.
    file name picture1.jpg

    how to read its metadata,size,description,etc.

    ReplyDelete
  17. the solution works well. thanks.
    btw how do i upload file with the custom types.
    (this has got additional metadata fields)

    ReplyDelete
  18. hi am not able find the project requesting you to send me on hiren.sojitra@gmail.com

    ReplyDelete
  19. I have again uploaded the project at https://skydrive.live.com/redir?resid=A482A6BCFDAABB97!121

    Thanks,

    -Anil

    ReplyDelete
  20. SQIAR (http://www.sqiar.com/solutions/technology/tableau) is a leading Business Intelligence company and provides Tableau Tutorial in United Kingdom and USA

    ReplyDelete
  21. I am getting 16 error while compiling the latest code. Sharing sample error.

    cannot convert from 'Alfresco.DictionaryServiceWebService.DictionaryService' 'Microsoft.Web.Services3.WebServicesClientProtocol'

    ReplyDelete