Console
The SimpleDataOperations sample uses a console based program to demonstrate the most common interactions you will make to an instance of Relativity Server. It shows how to:
- connect to the server
- retrieve a table from the server
- add a row to the retrieved table and apply that change to the server
- update/change the contents of a row and apply that change to the server
- delete a row from the table and apply that change to the server
Getting Started
The sample is typically located in C:\Users\Public\Documents\RemObjects Samples\Data Abstract for .NET\C#\SimpleDataOperations (Console)
, though you may have installed the Data Abstract for .NET SDK and it's samples in another location.
To build it, you will of course need Visual Studio, and like all the samples provided with Data Abstract for .NET you will need to be running Relativity Server with the DASamples
Domain and the Simple
Schema available.
Included with the sample is a DataTableExtension
class which has a single public static method (LogToString
) that takes a DataTable
object and creates a nicely formatted string representation of the table that can be printed to the Console which can be useful for debugging purposes. (An example of its output can be seen at the end of this document)
Running the Sample
When run the sample sets up a connection to an instance of Relativity Server and logs in (Step 1). It then retrieves the "Deps" table and prints the table to the console (Step 2). It then inserts a new row with a department name ("DepName") of "Security" and phone number ("DepPhone") of "(873)000-00-00" before applying those changes to the original data on the server (Step 3). The phone number is then updated to a new value (Step 4) before the new row is then deleted from the table (Step 5).
Examining the Code
All of the code can be found in the Program.cs
source file. The Deps
schema table used in this sample has three fields:
- DepId is the primary key and use an AutoInc data type.
- DepName is a string of up to 50 characters and is required for each new row
- DepPhone is also a string of up to 50 characters, the field is optional.
Setting up a Connection (Step 1)
You can think of setting up a connection to an instance of Relativity Server as requiring three stages.
In the first stage we initialize the underlying services & channels that will be used to establish the connection with on the server. To create the RemoteService object (which is part of the Remoting SDK), it needs to be passed an object that will handle the messaging between the client and the server, the next argument is communication channel to use, this represents the protocol that will be used. The next argument indicates whether the message should be cloned, and the final argument is the name of the remote service it should connect to. Here the message type used is BinMessage which will encode the messages in a highly efficient proprietary binary format. For the second argument for channel type we create an instance of IpHttpClientChannel and pass to it the web address to connect to as the TargetUrl. Lastly we pass "DataService" as the name of the service to connect to.
The second stage is to create and configure an instance of RemoteDataAdapter through which your code will directly interact with to communicate with the server. To it we must pass a DataStreamer which will be responsible for encoding and decoding the data packets to the DataStreamer property. The previously created RemoteService is passed to the RemoteService property.
The final stage is to create and pass a special login string (lLoginString
) to the LoginEx method of BaseLoginService to establish a login session. The LoginEx method returns a boolean which will be true
if the login attempt was successful or false
if it failed. To create an instance of BaseLoginService you can use the factory constructor RemObjects.DataAbstract.Server.BaseLoginService_Proxy
to which we pass the previously created message type and channel type objects, and the name of the login service to connect to (typically "loginService").
The login string is comprised of 4 parts:
- User Id the username to attempt to login with
- Password the password to use
- Domain the name of the domain that the schema is located in
- Schema the name of the schema we wish to use
For example, the login string from this sample is "User Id=simple;Password=simple;Domain=DASamples;Schema=Simple"
At this point the sample has a working connection to the server and is now logged in.
Console.WriteLine("Simple Data Operations Sample");
BinMessage lMessage = new BinMessage();
IpHttpClientChannel lChannel = new IpHttpClientChannel();
lChannel.TargetUrl = "http://localhost:7099/bin";
RemoteService lDataService = new RemoteService(lMessage, lChannel, true, "DataService");
Console.WriteLine(String.Format("Target URL is {0}", lChannel.TargetUrl.ToString()));
Console.WriteLine("RO SDK layer is configured.");
Bin2DataStreamer lDataStreamer = new Bin2DataStreamer();
RemoteDataAdapter lDataAdapter = new RemoteDataAdapter();
lDataAdapter.DataStreamer = lDataStreamer;
lDataAdapter.RemoteService = lDataService;
Console.WriteLine("RO DataAbstract layer is configured.");
Console.WriteLine("STEP 1: Login to DataService");
String RelativityConnectionStringTemplate = @"User Id=""{0}"";Password=""{1}"";Domain=""{2}"";Schema=""{3}""";
String lLoginString = String.Format(RelativityConnectionStringTemplate, "simple", "simple", "DASamples", "Simple");
Boolean lLogged = (new RemObjects.DataAbstract.Server.BaseLoginService_Proxy(lMessage, lChannel, "LoginService")).LoginEx(lLoginString);
Console.WriteLine(lLoginString);
if (lLogged)
{
Console.WriteLine("Login has been successful. Going further...");
Console.WriteLine();
}
else
{
Console.WriteLine("Cannot login. Please check your user name and password. Exiting...");
return;
}
Console.WriteLine("Simple Data Operations Sample")
var lMessage: BinMessage! = BinMessage()
var lChannel: IpHttpClientChannel! = IpHttpClientChannel()
lChannel.TargetUrl = "http://localhost:7099/bin"
var lDataService: RemoteService! = RemoteService(lMessage, lChannel, true, "DataService")
Console.WriteLine(String.Format("Target URL is {0}", lChannel.TargetUrl.ToString()))
Console.WriteLine("RO SDK layer is configured.")
var lDataStreamer: Bin2DataStreamer! = Bin2DataStreamer()
var lDataAdapter: RemoteDataAdapter! = RemoteDataAdapter()
lDataAdapter.DataStreamer = lDataStreamer
lDataAdapter.RemoteService = lDataService
Console.WriteLine("RO DataAbstract layer is configured.")
Console.WriteLine("STEP 1: Login to DataService")
var RelativityConnectionStringTemplate: String! = "User Id=\"{0}\";Password=\"{1}\";Domain=\"{2}\";Schema=\"{3}\""
var lLoginString: String! = String.Format(RelativityConnectionStringTemplate, "simple", "simple", "DASamples", "Simple")
var lLogged: Boolean! = RemObjects.DataAbstract.Server.BaseLoginService_Proxy(lMessage, lChannel, "LoginService").LoginEx(lLoginString)
Console.WriteLine(lLoginString)
if lLogged {
Console.WriteLine("Login has been successful. Going further...")
Console.WriteLine()
} else {
Console.WriteLine("Cannot login. Please check your user name and password. Exiting...")
return
}
Retrieving a Table (Step 2)
To retrieve a copy of a table from the schema we need to use the Fill method of the RemoteDataAdapter. It takes three arguments the first is the DataTable
that will contain the retrieved table data, the second argument is an instance of TableRequestInfo which provides a means of configuring what data is being retrieved. The last argument is a boolean which indicates if the schema should be included with the returned data.
In this sample a subclass (TableRequestInfoV5) of TableRequestInfo is used which makes it possible to use the Dynamic Select and Dynamic Where features of Data Abstract. Here the main thing of interest is the array of field names passed as strings to the DynamicSelectFieldNames which restricts the table data returned to only contain the specified fields.
NOTE Passing an invalid table name to the Fill
method will result in an ROException
being thrown which should be handled accordingly.
Console.WriteLine("STEP 2. Select the data (List of departments) ...");
RemObjects.DataAbstract.Server.TableRequestInfoV5 lRequestInfo = new RemObjects.DataAbstract.Server.TableRequestInfoV5();
lRequestInfo.MaxRecords = -1; // no limits - return all records
lRequestInfo.IncludeSchema = true; // update schema in the result data table to match recieved data (not necessarily).
lDataAdapter.DynamicSelect = true;
lRequestInfo.DynamicSelectFieldNames = new String[] { "DepId", "DepName", "DepPhone" };
DataTable lClientsData = new DataTable("Deps");
Boolean lApplySchema = true;
lDataAdapter.Fill(lClientsData, lRequestInfo, lApplySchema);
Console.WriteLine("STEP 2. Select the data (List of departments) ...")
var lRequestInfo: RemObjects.DataAbstract.Server.TableRequestInfoV5! = RemObjects.DataAbstract.Server.TableRequestInfoV5()
lRequestInfo.MaxRecords = -1
// no limits - return all records
lRequestInfo.IncludeSchema = true
// update schema in the result data table to match recieved data (not necessarily).
lDataAdapter.DynamicSelect = true
lRequestInfo.DynamicSelectFieldNames = (["DepId", "DepName", "DepPhone"] as? String![])
var lClientsData: DataTable! = DataTable("Deps")
var lApplySchema: Boolean! = true
lDataAdapter.Fill(lClientsData, lRequestInfo, lApplySchema)
Inserting a Row (Step 3)
Before adding a new row, a DataSet
needs to be created to contain all of the table data. After its created the previously retrieved DataTable
is passed to the Add
method of the Tables
collection.
To create a new table row use the NewRow
method of DataTable
which will return an empty DataRow
configured for the table. Then using the item properties to pass the values required for each column to the DataRow
. The last step is to add the new row to the table which is done by using the Add
method of the Rows
DataRowCollection
.
At this point the changes to the row are only in the local data. To apply those changes to the server you need to pass the previously created DataSet
object to the Update method of RemoteDataAdapter. This way any changes, be they modified column data, new rows and deleted rows, are passed to the server to be applied. The results from that application, plus any modifications the server data contained are return to the client application and applied to the local data.
The results of this are particularly observable in this sample with the new row. Initially the new row has a DepId
of "-1", once the changes have be applied to the server that DepId
is updated to reflect what the actual id is on the server.
If you don't apply the changes, or save the data in a Briefcase then those changes will be lost when the program exits.
Console.WriteLine("STEP 3. Insert a new row (Security department)...");
DataSet lDataSet = new DataSet();
lDataSet.Tables.Add(lClientsData);
DataRow lDataRow = lDataSet.Tables["Deps"].NewRow();
lDataRow["DepName"] = "Security";
lDataRow["DepPhone"] = "(873)000-00-00";
lDataSet.Tables["Deps"].Rows.Add(lDataRow);
Console.WriteLine(lClientsData.LogToString());
Console.WriteLine("Apply changes....");
try
{
lDataAdapter.Update(lDataSet);
Console.WriteLine(String.Format("Changes has been applied. New department has got an ID {0}", lDataRow["DepId"]));
}
catch (Exception Ex)
{
Console.WriteLine(Ex.Message);
}
Console.WriteLine("STEP 3. Insert a new row (Security department)...")
var lDataSet: DataSet! = DataSet()
lDataSet.Tables.Add(lClientsData)
var lDataRow: DataRow! = lDataSet.Tables["Deps"].NewRow()
lDataRow["DepName"] = "Security"
lDataRow["DepPhone"] = "(873)000-00-00"
lDataSet.Tables["Deps"].Rows.Add(lDataRow)
Console.WriteLine(lClientsData.LogToString())
Console.WriteLine("Apply changes....")
__try {
lDataAdapter.Update(lDataSet)
Console.WriteLine(String.Format("Changes has been applied. New department has got an ID {0}", lDataRow["DepId"]))
}
__catch Ex: Exception {
Console.WriteLine(Ex.Message)
}
Updating a Row (Step 4)
To change the value in a particular field/column access the field using the item property accessor of DataRow
. For example to change the phone number of the the new DataRow
, the column name is supplied as a string argument in []
brackets to the DataRow
, and the new value is assigned with a =
.
Remember that unless the local changes are applied back to the server using the Update they will be lost when the program is exited.
Console.WriteLine("STEP 4. Modify row (Change phone for added department)...");
lDataRow["DepPhone"] = "(873)456-77-77";
Console.WriteLine("Apply changes....");
try
{
lDataAdapter.Update(lDataSet);
Console.WriteLine("Changes has been applied");
}
catch (Exception Ex)
{
Console.WriteLine(Ex.Message);
}
Console.WriteLine("STEP 4. Modify row (Change phone for added department)...")
lDataRow["DepPhone"] = "(873)456-77-77"
Console.WriteLine("Apply changes....")
__try {
lDataAdapter.Update(lDataSet)
Console.WriteLine("Changes has been applied")
}
__catch Ex: Exception {
Console.WriteLine(Ex.Message)
}
Deleting a Row (Step 5)
Removing or deleting a row from a DataTable
is as simple as using the Delete
method of a particular DataRow
. If you don't have a reference to the particular row you can instead pass the row index to the RemoveAt
method of DataRowCollection
for example: lDataSet.Tables["Deps"].Rows.RemoveAt(4);
.
Remember that unless the local changes are applied back to the server using the Update they will be lost when the program is exited.
Console.WriteLine("STEP 5. Delete row (Remove added department)...");
lDataRow.Delete();
Console.WriteLine("Apply changes....");
try
{
lDataAdapter.Update(lDataSet);
Console.WriteLine("Changes has been applied");
}
catch (Exception Ex)
{
Console.WriteLine(Ex.Message);
}
Console.WriteLine("STEP 5. Delete row (Remove added department)...")
lDataRow.Delete()
Console.WriteLine("Apply changes....")
__try {
lDataAdapter.Update(lDataSet)
Console.WriteLine("Changes has been applied")
}
__catch Ex: Exception {
Console.WriteLine(Ex.Message)
}
Example Output
This is an example of the output you will see when the sample is run. In Step 2 you can see there are 2 rows in the Deps
table, in Step 3 there are now 3 rows and that in Step 4 the phone number for the Security department has changed. Lastly in Step 5 there are once again only 2 rows.
Simple Data Operations Sample
Target URL is http://localhost:7099/bin
RO SDK layer is configured.
RO DataAbstract layer is configured.
STEP 1: Login to DataService
User Id="simple";Password="simple";Domain="DASamples";Schema="Simple"
Login has been successful. Going further...
STEP 2. Select the data (List of departments) ...
Table: Deps (2 rows from 2)
|------------------------------------------------------|
| DepId | DepName | DepPhone |
|------------------------------------------------------|
| 1 | Sales | (873)456-78-99 |
| 2 | Incomes | (873)456-78-88 |
|------------------------------------------------------|
STEP 3. Insert a new row (Security department)...
Table: Deps (3 rows from 3)
|------------------------------------------------------|
| DepId | DepName | DepPhone |
|------------------------------------------------------|
| 1 | Sales | (873)456-78-99 |
| 2 | Incomes | (873)456-78-88 |
| -1 | Security | (873)000-00-00 |
|------------------------------------------------------|
Apply changes....
Changes has been applied. New department has got an ID 3
Table: Deps (3 rows from 3)
|------------------------------------------------------|
| DepId | DepName | DepPhone |
|------------------------------------------------------|
| 1 | Sales | (873)456-78-99 |
| 2 | Incomes | (873)456-78-88 |
| 3 | Security | (873)000-00-00 |
|------------------------------------------------------|
STEP 4. Modify row (Change phone for added department)...
Apply changes....
Changes has been applied
Table: Deps (3 rows from 3)
|------------------------------------------------------|
| DepId | DepName | DepPhone |
|------------------------------------------------------|
| 1 | Sales | (873)456-78-99 |
| 2 | Incomes | (873)456-78-88 |
| 3 | Security | (873)456-77-77 |
|------------------------------------------------------|
STEP 5. Delete row (Remove added department)...
Apply changes....
Changes has been applied
Table: Deps (2 rows from 2)
|------------------------------------------------------|
| DepId | DepName | DepPhone |
|------------------------------------------------------|
| 1 | Sales | (873)456-78-99 |
| 2 | Incomes | (873)456-78-88 |
|------------------------------------------------------|
Done!