-
Notifications
You must be signed in to change notification settings - Fork 16
Cookbook
Connections are created from a factory which determine the best connection object to create for a given URL. Once the connection is established, you can login using one of the credential patterns of AnonymousCredentials
, ExplicitCredentials
, or WindowsCredentials
. ExplicitCredentials
is universally supported while the other types currently have limited support.
C#
using Innovator.Client;
var conn = Factory.GetConnection("URL", "USER_AGENT");
conn.Login(new ExplicitCredentials("DATABASE", "USER_NAME", "PASSWORD"));
All Aras objects (items, results, exception, etc.) are created through an ElementFactory
. This class handles the serialization of base data types relative to a particular connection (e.g. handling timezone conversions for DateTime
values). An ElementFactory
can be obtained for a connection via the AmlContext
property of the IConnection
interface. Alternatively, if you want to manipulate AML outside of the context of a server connection, you can assume the local timezone and use the local factory by calling ElementFactory.Local
.
C#
var aml = conn.AmlContext;
IItem myItem = aml.Item(aml.Type(myType), aml.Action(myAction));
IResult myResult = aml.Result(resultText);
ServerException = aml.ServerException(errorMessage);
OR
var aml = ElementFactory.Local;
IItem myItem = aml.Item(aml.Type(myType), aml.Action(myAction));
IResult myResult = aml.Result(resultText);
ServerException = aml.ServerException(errorMessage);
There are a few ways to get an IReadOnlyItem
when you know its id and type, the simplest being the IConnection.ItemById()
extension method. However, overuse of this method can lead to poor performance as it select more data than you likely need. Therefore, If you need to be granular about your request then building the AML is required. This provides the ability to include controls to limit the results and define the structure to be returned for the Items found.
C#
var results = conn.Apply(@"<Item type='@0' action='@1' id='@2' />"
, myType, myAction, myId);
var item = results.AssertItem();
// NOTE: An exception will be thrown if there is not a single item in the result
OR
var aml = conn.AmlContext;
IItem qryItem = aml.Item(aml.Type(myType), aml.Action(myAction), aml.Id(myId));
var results = qryItem.Apply(conn);
var item = results.AssertItem();
// NOTE: An exception will be thrown if there is not a single item in the result
OR
var item = conn.ItemById(myType, myId);
// NOTE: An exception will be thrown if there is not a single item in the result
There is no difference in setting up a query for a single Item or for many. Only the criteria define the set size returned. In this recipe you apply an AML statement and iterate over the Items returned producing a HTML <TABLE>
fragment.
C#
// Configure and execute the query using one of the two methods below:
var results = conn.Apply(@"<Item type='Part' action='get' select='item_number,description,cost'>
<cost condition='gt'>100</cost>
</Item>");
// Build the table
var content = new System.Text.StringBuilder("<table>");
foreach (var item in results.Items())
{
content.Append("<tr>");
content.Append("<td>").Append(item.Property("item_number").Value).Append("</td>");
content.Append("<td>").Append(item.Property("description").Value).Append("</td>");
content.Append("<td>").Append(item.Property("cost").Value).Append("</td>");
content.Append("</tr>");
}
content.Append("</table>");
return aml.Result(content.ToString());
To query for an Item and retrieve its structure you build the query as the structure you want returned. Use the methods to add the relationships you want and build the structure in the Item. The server returns the structure that follows the request structure.
This recipe illustrates several related concepts together, which are how to get a set of Items from an Item and how to iterate over the set, plus how to get the related Item from the relationship Item.
C#
// Configure and execute the query using one of the two methods below:
var results = conn.Apply(@"<Item type='Part' action='get' select='item_number,description,cost' id='@0'>
<Relationships>
<Item type='Part BOM' action='get' select='quantity,related_id(item_number,description,cost)' />
</Relationships>
</Item>", myId);
// NOTE: No need to check for an error. If there is not a single item in the result, an useful
// exception will be thrown which will tell you the error returned by the server (if there was
// one). However, if you are exception-adverse, you can always check to see if the
// results.Exception property is null
var bomItems = results.AssertItem().Relationships();
// Create the results content
var content = new System.Text.StringBuilder(@"<table border='1'>
<tr>
<td>Part Number</td>
<td>Description</td>
<td>Cost</td>
<td>Quantity</td>
</tr>");
// Iterate over the BOM Items
foreach (var bom in bomItems)
{
var bomPart = bom.RelatedItem();
content.Append("<tr>");
content.Append("<td>").Append(bomPart.Property("item_number").Value).Append("</td>");
content.Append("<td>").Append(bomPart.Property("description").Value).Append("</td>");
content.Append("<td>").Append(bomPart.Property("cost").Value).Append("</td>");
content.Append("<td>").Append(bom.Property("quantity").Value).Append("</td>");
content.Append("</tr>");
}
content.Append("</table>");
return aml.Result(content.ToString());
You want to perform a query using the AML to construct the query criteria.
C#
IConnection conn;
var partNumberStart = "1";
var qtyLowerBound = 1;
var items = conn.Apply(@"<Item type='Part' action='get' select='item_number,description,cost'>
<item_number condition='like'>@0</item_number>
<Relationships>
<Item type='Part BOM' action='get' select='quantity'>
<quantity condition='gt'>@1</quantity>
</Item>
</Relationships>
</Item>", partNumberStart + "%", qtyLowerBound).Items();
foreach (var item in items)
{
}
You want to get the next promotion states for an Item and use the states as the choices for a dropdown
control. Use the item action getItemNextStates
to get the next state values.
C#
IConnection conn;
IReadOnlyItem item;
var transitions = conn.Apply("<Item type='@0' id='@1' action='getItemNextStates'/>"
, item.TypeName(), item.Id()).Items();
var stateNames = transitions
.Select(t => t.Property("to_state").AsItem().Property("name").Value)
.ToArray();
You want to search for an Item using the relationship and related Items as search criteria. In this recipe, we get the User Item that matches the Identity name as an Alias relationship to the User Item.
C#
IConnection conn;
var users = conn.Apply(@"<Item type='User' action='get' select='first_name,last_name,email'>
<Relationships>
<Item type='Alias' action='get' select='related_id'>
<related_id>
<Item type='Identity' action='get' select='keyed_name'>
<name>Larry Bird</name>
</Item>
</related_id>
</Item>
</Relationships>
</Item>").Items();
You want to add an Item configuration like a BOM as one transaction
C#
var parentPart = "123-456";
var description = "Blah blah";
var qty = 10;
var childPart = "555-555";
IConnection conn;
var resultItem = conn.Apply(@"<Item type='Part' action='add'>
<item_number>@0</item_number>
<description>@1</description>
<Relationships>
<Item type='Part BOM' action='add'>
<quantity>@2</quantity>
<related_id>
<Item type='Part' action='get'>
<item_number>@3</item_number>
</Item>
</related_id>
</Item>
</Relationships>
</Item>", parentPart, description, qty, childPart).AssertItem();
You want to add a new named Permission Item.
C#
private void Main()
{
IConnection conn;
var aml = conn.AmlContext;
var permItem = aml.FromXml(@"<Item type='Permission' action='add'>
<name>AK Part Permissions</name>
</Item>").AssertItem();
SetIdentityAccess(permItem, "All Employees", "get", true);
SetIdentityAccess(permItem, "CM", "get", true);
SetIdentityAccess(permItem, "CM", "update", true);
SetIdentityAccess(permItem, "CM", "delete", true);
permItem.Apply(conn).AssertNoError();
}
private void SetIdentityAccess(IItem item, string identityName, string permType, bool accessState)
{
var access = item.AmlContext.FromXml(@"<Item type='Access' action='add'>
<related_id>
<Item type='Identity' action='get'><name>@0</name></Item>
</related_id>
</Item>", identityName).AssertItem();
access.Property("can_" + permType).Set(accessState);
item.Relationships().Add(access);
}
You want to set a new private Permission for an Item.
C#
private void Main()
{
IConnection conn;
var aml = conn.AmlContext;
// Run the query. If there are errors, an exception will be thrown
var items = conn.Apply(@"<Item type='Part' action='get' select='id,permission_id'
expand='1' levels='1'>
<item_number condition='like'>123%</item_number>
</Item>").Items();
// Iterate over the Items returned and add the private permissions for each.
// The assumption is that the items already have private permissions, and we are merely
// modifying the access list
foreach (var item in items)
{
var permItem = item.PermissionId().AsItem();
var editQuery = aml.Item(aml.Type("Permission"), aml.Action("edit")
, aml.Id(permItem.Id())
, aml.Property("name", permItem.Id()));
// remove existing permissions first
editQuery.Relationships()
.Add(permItem.Relationships("Access").Select(r => aml.Item(
aml.Type("Access"), aml.Id(r.Id()), aml.Action("delete")
)));
SetIdentityAccess(editQuery, "Component Engineering", "get", true);
SetIdentityAccess(editQuery, "CM", "get", true);
SetIdentityAccess(editQuery, "CM", "update", true);
var myAlias = conn.Apply(@"<Item type='Alias' action='get' select='related_id(id,name)'>
<source_id>@0</source_id>
</Item>", conn.UserId).AssertItem().RelatedItem();
SetIdentityAccess(editQuery, myAlias.Property("name").Value, "get", true);
editQuery.Apply(conn).AssertNoError();
}
}
private void SetIdentityAccess(IItem item, string identityName, string permType, bool accessState)
{
var access = item.AmlContext.FromXml(@"<Item type='Access' action='add'>
<related_id>
<Item type='Identity' action='get'><name>@0</name></Item>
</related_id>
</Item>", identityName).AssertItem();
access.Property("can_" + permType).Set(accessState);
item.Relationships().Add(access);
}
Generic methods can be applied by observing that all you need to do is set the action
attribute to the name of your method. In this example, assume a server-side method named "Reverse String" exists, and that it returns a result item containing the reversed contents of the <string>
tag.
C#
var results = conn.Apply(@"<Item type='Method' action='Reverse String'>
<string>abc</string>
</Item>");
return aml.Result(results.Value);
Use the IConnection.ApplySql()
extension method to submit SQL direct to the database. The format of the XML returned by the IConnection.ApplySql()
method when the SQL statement is a select
statement is:
<SOAP-ENV:Envelope xmlns:SOAP-ENV=...>
<SOAP-ENV:Body>
<ApplySQLResponse>
<Item>
<A>aval</A>
<B>bval</B>
…
</Item>
<Item>
…
</Item>
…
</ApplySQLResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
In case executed SQL statement doesn’t return a record set (e.g. update [table] …), the returned AML either contains a <Fault>
if SQL statement failed or looks like:
<SOAP-ENV:Envelope xmlns:SOAP-ENV=...>
<SOAP-ENV:Body>
<ApplySQLResponse>
OK
</ApplySQLResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This recipe returns the XML from the ApplySQL()
method and forms HTML for a table to display the data.
C#
var results = conn.ApplySQL(@"select login_name,first_name,last_name,email
from [user]
order by last_name,first_name");
var content = new System.Text.StringBuilder(@"<style type='text/css'>
table {background:#000000;}
th {font:bold 10pt Verdana; background:#0000FF; color:#FFFFFF;}
td {font:normal 10pt Verdana; background:#FFFFFF;}
caption {font:bold 14pt Verdana; text-align:left;}
</style>
<table id='tbl' border='0' cellspacing='1' cellpadding='2' datasrc='#itemData'>
<caption>User Directory</caption>
<thead>
<tr>
<th>Login Name</th>
<th>First Name</th>
<th>Last Name</th>
<th>EMail</th>
</tr>
</thead>
<tbody>");
foreach (var user in results.Items())
{
content.Append("<tr>");
content.Append("<td>").Append(user.Property("login_name").Value).Append("</td>");
content.Append("<td>").Append(user.Property("first_name").Value).Append("</td>");
content.Append("<td>").Append(user.Property("last_name").Value).Append("</td>");
content.Append("<td>").Append(user.Property("email").Value).Append("</td>");
content.Append("</tr>");
}
content.Append("</tbody>");
content.Append("</table>");
return conn.AmlContext.Result(content.ToString());
You want to get the Identity ID's for the User.
C#
IConnection conn;
var idents = conn
.Apply(new Command("<Item/>").WithAction(CommandAction.GetIdentityList))
.Value
.Split(',');
In order to have the AML submitted to the vault server, you cannot use a simple Command
object (which was implicitly created from strings in all of the previous examples). Rather, you need to create an UploadCommand
as shown in the code below.
C#
var upload = conn.CreateUploadCommand();
var fileAml = upload.AddFile(@"C:\My Document.doc");
// Note, the ! after the @0! tells the library to inject the raw XML without encoding it.
upload.WithAml(@"<Item type='Document' action='add'>
<item_number>123</item_number>
<Relationships>
<Item type='Document File' action='add'>
<related_id>@0!</related_id>
</Item>
</Relationships>
</Item>", fileAml);
conn.Apply(upload).AssertNoError();
// You can also upload a file from a stream if it does not exist on disk:
var memStream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes("FILE_CONTENT"));
var upload = conn.CreateUploadCommand();
var fileAml = upload.AddFile(@"C:\My Document.doc", memStream);
upload.WithAml(@"<Item type='Document' action='add'>
<item_number>123</item_number>
<Relationships>
<Item type='Document File' action='add'>
<related_id>@0!</related_id>
</Item>
</Relationships>
</Item>", fileAml);
conn.Apply(upload).AssertNoError();
To download a file, use the custom "SOAP" action of DownloadFile
.
C#
var stream = conn.Process(new Command("<Item type='File' action='get' id='@0' />", fileId)
.WithAction(CommandAction.DownloadFile));
This is similar to the last recipe, but uses the ItemByKeyedName()
extension method to get an existing File
Item and copyAsNew
action to create it as a new File Item.
C#
// Create the Document item
var aml = conn.AmlContext;
var docItem = aml.Item(aml.Type("Document"), aml.Action("add"), aml.Property("item_number", "456"));
// Get the File Item. An except will be thrown if necessary.
var fileItem = conn.ItemByKeyedName("File", "My Document.doc").AssertItem();
// Duplicate File Item as files should be 1 to 1
// You need to clone the item to get an editable one
var fileCopyQuery = fileItem.Clone();
fileCopyQuery.Action().Set("copyAsNew");
// Create the Relationship
docItem.Add(aml.Relationships(
aml.Item(aml.Type("Document File"), aml.Action("add"), fileCopyQuery)
));
var results = docItem.Apply(conn);
// Act on the results
Use the Pre Server Method on the Life Cycle Transition to call a server side Method to validate the Item before it is promoted and if invalid rejects the Promote by returning an Error Item.
C#
var item = arg.Item;
if (item.Property("cost").AsDouble(0.0) > 500)
{
throw arg.Conn.AmlContext.ServerException("Error promoting: Item costs more than $500.00");
}
return item;
Use the library's ability to convert value for you.
C#
// Get yesterday's date
var myDate = DateTime.Now.AddDays(-1);
// Find all methods edited in the past 24 hours
var results = conn.Apply(@"<Item type='Method' action='get' select='name'>
<modified_on condition='gt'>@0</modified_on>
</Item>", myDate);
// loop through the returned methods and return the list
var methodList = new System.Text.StringBuilder();
foreach (var method in results.Items())
{
// Yes, this if statement isn't necessary, but it does demonstrate
// use of the code
if (method.ModifiedOn().AsDateTime(DateTime.MinValue) > myDate)
{
methodList.Append(method.Property("name").Value).Append(", ");
}
}
In order to support .Net 3.5 without any external dependencies and to better match JQuery, the library implements a Promise API for implementing asynchronous calls. However, for better compatibility with other async code, the .Net library also supports converting promises to Tasks.
C#
// .Net 3.5
var promise = conn.ApplyAsync("MY_QUERY", true, false)
.Done(result => {})
.Fail(ex => {});
// .Net 4
var result = await conn.ApplyAsync("MY_QUERY", true, false);
When traversing a long tree, it nice not to have to deal with null reference exceptions. Therefore, the library implements the null-object pattern. Simply check the Exists property to determine if a retrieved property actually exists.
C#
var firstNameProp = part.CreatedById().AsItem().Property("first_name");
if (!firstNameProp.Exists)
{
// Get the first_name by other means
}
You have control over the HTTP headers sent with each request to the server. To modify particular headers, follow a pattern similar to
C#
var conn = Factory.GetConnection("URL", "USER_AGENT");
conn.DefaultSettings(r => r.SetHeader("X-CUSTOM-HEADER", "MY_VALUE"));
conn.Login(new ExplicitCredentials("DATABASE", "USER_NAME", "PASSWORD"));