Archive for September, 2009

Setting up your Mobile SalesLogix Development Environment

Posted by Jason Huber on September 10, 2009
Administrator, Developer / No Comments

So you are starting to work on developing in SalesLogix Mobile. Perhaps you heard that there is an iPhone app in the works or that we support the storm or you just want to get it onto every Black Berry in the company. You need to make some adjustments to the OOTB Mobile installation and want to know where to get started.

First you need to take the developing for Mobile class from Sage.

This will give you a good footing on what is possible with Mobile and how to do the most common customizations.

But really I wanted to talk about the setup of your environment. To customize the mobile platform you need to be able to compile Java and windows code, deploy it and then have an emulator or simulator to test it on. In training we borrowed some documents from the Mobile team just after they were acquired and had the basic setup listed in the book. Diane tweaked this a bit and streamlined it so that it would work in the classroom. Shortly after I started working at Sage a service pack was released for mobile, I looked at the setup requirements and knowing that I build many, many virtual machines I said “no way am I installing all that each time”. So I started on what I call Jasons Mobile Setup.

I used an open source setup tool called Inno Setup. This gives me an iss file to work with. The contents of the files are:

The title and version of the resulting exe:
[Setup]
AppName=Jason Mobile Setup
AppVerName=5.5
DefaultDirName=C:\Jasons_Mobile_Setup
OutputDir=H:\Jasons_Mobile_Install
OutputBaseFilename=Jasons_Mobile_Setupv5.5

The files I want IN the setup:
[Files]
Source: WindowsMobilePowerToys.msi; DestDir: {app}
Source: standalone_emulator_V1.exe; DestDir: {app}
Source: activeSyn45.msi; DestDir: {app}
Source: efp.msi; DestDir: {app}
Source: BlackBerry_Email_MDS_4.1.4.exe; DestDir: {app}
Source: BlackBerry_JDE_4.2.1.exe; DestDir: {app}
Source: antenna-bin-0.9.15-beta.jar; DestDir: c:\program files\ant\lib
Source: netsvwrap.msi; DestDir: {app}
Source: jdk-1_5_0_07-windows-i586-p.exe; DestDir: {app}
Source: j2me_wireless_toolkit-2_2-windows.exe; DestDir: {app}
Source: BlackBerry_Simulators_4.2.1.85_8700.exe; DestDir: {app}
Source: Desktop_Software_v4.2_SP2.exe; DestDir: {app}
Source: vjredist.exe; DestDir: {app}
Source: paths.bat; DestDir: {app}
Source: apache-ant-1.7.0-bin\ant\bin\ant; DestDir: {pf}\ant\bin
Source: apache-ant-1.7.0-bin\ant\bin\ant.bat; DestDir: {pf}\ant\bin

(truncated because it goes on for lines and lines with all the ant files..

And the order of execution:
[Run]
Filename: msiexec.exe; Parameters: "/i ""{app}\netsvwrap.msi"""
Filename: {app}\standalone_emulator_V1.exe
Filename: msiexec.exe; Parameters: "/i ""{app}\efp.msi"""
Filename: msiexec.exe; Parameters: "/i ""{app}\activeSyn45.msi"""
Filename: msiexec.exe; Parameters: "/i ""{app}\WindowsMobilePowerToys.msi"""
Filename: {app}\jdk-1_5_0_07-windows-i586-p.exe
Filename: {app}\Desktop_Software_v4.2_SP2.exe
Filename: {app}\BlackBerry_Simulators_4.2.1.85_8700.exe
Filename: {app}\j2me_wireless_toolkit-2_2-windows.exe
Filename: {app}\BlackBerry_Email_MDS_4.1.4.exe
Filename: {app}\BlackBerry_JDE_4.2.1.exe
Filename: {app}\vjredist.exe
Filename: {app}\paths.bat

Then I run the iss file through inno setup and I get an exe.

The only real catch (and this is documented in the developers subscription and in the class book) is that the paths are not updated properly in windows 2003 and xp, so I need to edit those manually:

SETX JAVA_HOME "C:\Program Files\Java\jdk1.5.0_07" /M
SETX JAVA_PATH "C:\Program Files\Java\jdk1.5.0_07" /M
SETX ANT_HOME "c:\program files\ant" /M
SET PATH="%PATH%;%ANT_HOME%\lib;%JAVA_HOME%\bin" /M

you can tell what I am doing there.

So why all the files? They are either needed for compilation or for emulation/simulation of the BB or Mobile device. Desktop Manager is in there, Sync is in there and everything else you need except the files needed from SalesLogix specifically.

Hopefully this helps if you have planned your own development environment for mobile you can download Jasons Mobile Setup.exe and at least extract it to the temporary location of your choice and use the files from there if you do not need both the BB and Windows Mobile setup files.

Here is a link to the whole ISS file.5.5.iss

Tags: , , , , , , ,

Process Orchestration Uses and Types

Posted by Jason Huber on September 08, 2009
Developer / 1 Comment

BugsProcessOrchestration

What about KnowledgeSync?

The first thing that I get asked about Process Orchestration is: Does it replace KnowledgeSync. The answer is: “It depends”. It really does depend on how you use KnowledgeSync. Can Process Orchestration do some of the things that KnowledgeSync does? Yes. Does it do them all in the same manner? no and no.

First Process Orchestration is hosted through the Process Host portal from SalesLogix. This makes it a web application. Web applications are meant to be accessed via a web protocol like http. The reason this is important to understand is that web applications are stateless and have no “scheduled task” abilities. This means you cannot schedule them to run at a certain time and day.

Moving past all of that we have a few pieces to Process Orchestration:

  • DB Eventing – used to track changes to the database and “kick off” the appropriate tasks in PO
  • Process Host Portal – this is used as an integration point for the web clients and db eventing
  • The Process Libraries themselves – these are the bits you pull into Visual Studio to create an actual process of your own

I am sure I am missing something, so comment and I will edit the post

So PO is not a replacement for KnowledgeSync and you cannot run it like a scheduled task, so what is it good for? The next question is – it is a replacement for Contact Processes or Sales Processes? Again the answer is it can be and it is not.

What can you do with PO?

Without going into code or showing any videos, which are all available in the developers subscription or the advanced web developers course, let’s see what we can do with Process Orchestration:

You can:
(lets check out the processstart.xml file to see what you CAN do:
<!--
This file is used to configure the execution of workflows based on data events.

<event name="{EVENT TYPE}">
<entityType name="{ENTITY TYPE}" memberFilter="{PROPERTY LIST}">
<runProcess name="{WORKFLOW NAME}" />
</entityType>
</event>
<event ...>

{EVENT TYPE} indicates the type of notification for which to run the configured workflows
Created => Raise an event when a new entity is created
Deleted => Raise an event when an existing entity is deleted
PropertyChanged => Raise an event when an existing entity is updated, based on the subscribed properties in the memberFilter attribute

{ENTITY TYPE} should be a fully qualified .NET type name indicating the entity to watch
1 or more elements may be present.

{PROPERTY LIST} is a comma separated list of properties on the {ENTITY TYPE} that are being watched for changes.
It is important to note that only properties mapped to database columns can be watched.
These entries are case sensitive.

{WORKFLOW NAME} specifies the class name of the workflow to be executed.
1 or more elements may be present.
The processLib.xml must be configured with the appropriate assembly which this workflow is contained within.

-->
- <!-- Example

<event name="PropertyChanged">
<entityType name="Sage.Entity.Interfaces.IAccount, Sage.Entity.Interfaces" memberFilter="Type,SubType,AccountName">
<runProcess name="AccountTypeChangedWorkflow" />
</entityType>
</event>

-->
- <event name="PropertyChanged">
- <entityType name="Sage.Entity.Interfaces.IAccount, Sage.Entity.Interfaces" memberFilter="Division,AccountName">
<runProcess name="Workflow1" />
</entityType>
</event>
</processStartConfiguration>

So you can see:
Created => Raise an event when a new entity is created
Deleted => Raise an event when an existing entity is deleted
PropertyChanged => Raise an event when an existing entity is updated, based on the subscribed properties in

How does it know where your workflow or process is? That is in the processLib.xml:

<?xml version="1.0" encoding="utf-8" ?>
- <processLib>
- <!-- <assembly name="WorkflowTestLib" />
-->
<assembly name="MyWorkflows" />
</processLib>

This means you need to place your process in the bin directory of the process host portal in order for it to be utilized.

What will this look like? Nothing. It will only run when something changes in :
- <entityType name="Sage.Entity.Interfaces.IAccount, Sage.Entity.Interfaces" memberFilter="Division,AccountName">

The Account.Division or Account.AccountName

Simple as that. It will run from start to finish and do whatever it is to do once.

Now this is just a simple sequential flow. Starting on slide 75 of the following linked document:
http://download.saleslogix.com/v75/SalesLogix_v75_SneakPeek_July2008.ppt

You can see that there are also Goal driven processes in the toolbox:ProcessOrchestrationToolbox7.5.1

These appear in the web client in the area normally used for the tasks area (bottom right). The data for these goal driven processes is stored in the database and persisted from session to session. This means if you need to change a goal driven process that is currently “in process” by anyone, all of that data must be removed – by you.
ProcessOrchestrationGoalinWeb7.5.1

So does PO replace KS? no. But it can do a lot. Check out either the adv. web class and/or the developers subscription for more details. I will probably post more information as the 7.5.2 PO enhancements make their way into my radar.

Tags: , , , ,

Setting up SalesLogix sData and Process Orchestration – explained

Posted by Jason Huber on September 04, 2009
Developer / No Comments

So we want to setup sData and Process Orchestration and reading the installation guides we see that we need to change some settings in IIS and an additional setting in the SalesLogix Administrator.

But what are we actually changing here and why?

sDataandPO_IISSettings1
First we need to ensure that all requests, not already mapped are sent to the isapi.dll. Why? Because IIS by default will only “handle” or do something with requests that it understands. sData sends in requests that are not understandable by IIS 6.0 out of the box. Stuff like: http://localhost:3333/sdata/slx/dynamic/-/accounts is not something IIS understands out of the box. IIS does understand http://localhost:3333/slxclient/accounts.aspx (notice the .aspx) in the file name.

So we tell IIS to handle all other request (wildcards) using the Isapi filter. We tell it to NOT check to see if the file exists. We aren’t checking to see if the isapi.dll exists, we are checking to see if /accounts exists as in http://localhost:3333/sdata/slx/dynamic/-/accounts which is does not. If you are wondering about the reason for the interesting link then you should check out RESTful web services.

Why do we tell it to process these requests using the isapi.dll instead of some saleslogix dll? Because the isapi.dll will look in the web.config for the application on which the request was made for an httphandler. We have one entry in our sData web.config for this purpose:

Why disable Integrated Authentication?

sDataandPO_IISSettings2
Now we are removing the Integrated Windows Authentication. The reason for this is that sData is not likely to have someone browsing to the website. Even if Lee is logged into the machine, it is unlikely that it is actually Lee going to the sData portal for his data. It is probably an application working on the request of Lee.

So like IE performs for us, we want Lee to be asked for his credentials so that we can filter the sData results by Lee’s security settings. So remove that setting and Lee will be asked for his credentials and these will be authenticated against the SalesLogix database. But wait! Our SalesLogix website (sdata portal) runs under a “webdll” user. This user has no permissions on the SalesLogix db through the provider to access Lee’s user record and verify his username and password. So how do we get the website into the database to see if what Lee enters at the username and password prompt is correct?

sDataandPO_adminsettings

Go into the Administrator

This is why we have to go into the administrator and change the settings there. We map a user – usually admin – to our “webdll” user. This says that when webdll, acting on behalf of the sdata portal (as setup in IIS under the identity tab) requests to check Lee’s credentials it will be admin who actually logs into SalesLogix and checks to see if Lee entered them correctly.

So we covered the three pieces.

  1. IIS, Config button
  2. IIS, Security Tab
  3. SLX Administrator

And we should now know why we do what we do in each.

But what could possibly go wrong?

Perhaps you forgot to set the appropriate settings in the administrator you will see db_sec_auth_failed or something similar:
sDataandPO_adminnotsettowebdll

Perhaps you forgot to map the dll? You will just see page cannot be found.

If you do not see the “SalesLogix Client” as in the following screen shot then you have likely missed a step in IIS:
sDataandPO_askingmeforcredsOK

I know we mainly referred to sData in this post, but the same settings apply to Process Orchestration or the Process Host portal. Remember the sData spec and more information can be found here: http://interop.sage.com

This setup stuff generally is an admin task, but developers need to be familiar with it as well.
So be sure to check out the videos on the topic in both the Administrators Subscription and Developers Subscription here:
http://sageu.com/saleslogix/subscriptions/. You can just search for “sData” or “Process” and will find several videos on the topic.

Tags: , , ,

IRepository and ICriteria Basics

Posted by Diane Kohnert on September 03, 2009
Developer / 1 Comment

Here are a few basics using IRepository and ICriteria to query data in slx web.

Basic select statement

The most basic use of IRepository is using the Repository to return all rows back. This code shows getting a Repository for Products and then using the repository FindAll method to return all rows from the products entity.

Sage.Platform.Repository.IRepository
rep = Sage.Platform.EntityFactory.GetRepository();
//return ALL products
System.Collections.Generic.IList results = rep.FindAll();

Adding order by

In this code example we get the Product repository back but then need to also setup ICriteria to add an order by to our result. Once we have a criteria setup which we do below very easily because of our use of the RepositoryHelper that gives us access to CreateCriteria right from the repository.
Finally we call the AddOrder method off of criteria, AddOrder is expecting a parameter of type IOrder. We can use the ExpressionFactory, (EF) provided for use again because of the RepositoryHelper, to add the Asc order by to our query.

Sage.Platform.RepositoryHelper rep =
Sage.Platform.EntityFactory.GetRepositoryHelper();

Sage.Platform.Repository.ICriteria criteria = rep.CreateCriteria();

criteria.AddOrder(rep.EF.Asc("Name"));
System.Collections.IList results = criteria.List();

Jason talks more about the RepositoryHelper and ICriteria in his post
using-icriteria-like-ado-and-some-hql-too

Basic select with criteria

In this code we are setting up another criteria but this type with a where clause. In this example the query is something like this in SQL – select * from Account Where accountid = ‘some passed in ID’. But the big different in the method we are used to using SQL and the criteria we will setup using IRepository and ICriteria is the idea that we say where account entity = pass in account entity. We compare entities instead of ID’s… you can see that in the
criteria.Add(rep.EF.Eq("Account", account));
statement below notice how the parameters are “Account” and account. The “Account” is actually the name of the entity (or relationship) as it appears in Application Architect (AA). The lowercase account is actually a passed in variable or in our case a variable we setup in the first line of code by getting the current bindingsource off the form.

Sage.Entity.Interfaces.IAccount account =
BindingSource.Current as Sage.Entity.Interfaces.IAccount;

Sage.Platform.RepositoryHelper rep =
Sage.Platform.EntityFactory.GetRepositoryHelper();

Sage.Platform.Repository.ICriteria criteria = rep.CreateCriteria();

criteria.Add(rep.EF.Eq("Account", account));
System.Collections.IList results = criteria.List();

Creating a join

Use ICriteria CreateAlias method to create a join to another entity via an Application Architect relationship. In this case we have created a relationship between Account and Product in the Application Architect called AssociatedProducts. Then we use the CreateAlias method to setup that join, notice that the first parameter is the actual relationship name (from AA), the second parameter is our alias just for use in the code – these two could be named the same thats no problem. I just named them differently here to point out the difference between the two parameters. I combined the criteria.CreateAlias and the criteria.Add statements together just to show another possibility, but you certainly could seperate the two statements on two separate lines of code…

Sage.Entity.Interfaces.IAccount account =
BindingSource.Current as Sage.Entity.Interfaces.IAccount;

Sage.Platform.RepositoryHelper rep =
Sage.Platform.EntityFactory.GetRepositoryHelper();

Sage.Platform.Repository.ICriteria criteria = rep.CreateCriteria();

criteria.CreateAlias("AssociatedProducts","associatedproducts").Add(rep.EF.Eq("associatedproducts.Account", account));

System.Collections.IList results = criteria.List();

Setting a Projection

(used for aggregations or things like distinct)
We need to use the ProjectionFactory if we want to use a distinct in our select statement. In this case we are returning a distinct set of Products. We use criteria to get a ProjectionList then use the ExpressionFactory to setup each item in that projectionlist. You can think of the projectionlist as the items that you want in your select statement. If you only have one item in the projectionlist you can just add the projection as seen in with criteria.SetProjection(rep.PF.Distinct(Rep.PF.Property("Name"))); Or if you have multiple items in the projectionlist you add them using criteria.Add().


Sage.Platform.RepositoryHelper rep =
Sage.Platform.EntityFactory.GetRepositoryHelper();

Sage.Platform.Repository.ICriteria criteria = rep.CreateCriteria();

criteria.SetProjection(rep.PF.Distinct(Rep.PF.Property("Name")));
//OR
criteria.SetProjection(rep.PF.Distinct(Rep.PF.ProjectionList());
criteria.Add(rep.PF.Property("Name"));

Tags: , , , , , , , , , , , ,

sData in 7.5.2 (sData 1.0 Spec) and sData Client Libraries

Posted by Jason Huber on September 03, 2009
Developer / 2 Comments

There is a lot of talk right now about sData in 7.5.2 and the new client libraries that are being released. These libraries are designed to make accessing sData from SalesLogix easier, well really any sData source easier, but we are talking SalesLogix here. The idea is that the libraries will abstract some of the more common tasks that right now require you to write a lot of code and more importantly understand reading and writing xml. Why xml? because it is extensible of course, but really because sData is just an ATOM feed and ATOM is in xml format.
The important things to remember:

  1. 7.5.1 uses sData 0.9
  2. 7.5.2 use sData 1.0
  3. These client Libraries from the Gobi team are used for 7.5.2 and sData 1.0

You can find out more about sData at: http://interop.sage.com/

So how can we use these new sData client libraries? Well at the BootCamp in 2009 we did just that:

First we need the libraries (these are available as part of the 7.5.2 beta, so just ask).
Add a reference:
sDataclientLibrariesInReferences

Then I added some using statements:

using Sage.SData.Client.Atom;
using Sage.SData.Client.Common;
using Sage.SData.Client.Core;
using Sage.SData.Client.Extensions;
using System.Xml;

Then we need a couple form level variables (or at least this is how I did it):

//I put this up here because I need it in two methods.
// create the SDataService with url, user name, and password
SDataService _sdataService = new SDataService(@"http://localhost:3333/sdata/slx/dynamic/-/", "Lee", "");
SDataSingleResourceRequest _SingleResource;

Notice in the above code the new /-/ – that is an important change with sData. There is now a Payload in the feed. This is a breaking change for your 7.5.1 sData code.

So we have the dataservice created, now we need to actually initialize it:
// initialize it
_sdataService.Initialize();

Now that we have it initialized we can do many things, the two I have worked with thus far are:

SDataResourceCollectionRequest
//and
SDataSingleResourceRequest

Which really seem to correlate to query and read here:
http://interop.sage.com/daisy/sdata/Introduction.html

Anyway, if we want to get all accounts back:

SDataResourceCollectionRequest myrequest = new SDataResourceCollectionRequest(_sdataService);
myrequest.ResourceKind = "Accounts";
AtomFeed myFeed = myrequest.Read();

What do these look like at this point? An atom feed. You can hover over myFeed and read it in text or xml format and see it for yourself. What we want to do is load it into some sort of control. To do that we need to get it into a bindable object like a datatable:

DataTable myTable = new DataTable();
myTable.Columns.Add("Uri");
myTable.Columns.Add("AccountName");

foreach (var atomentry in myFeed.Entries)
{
var dr = myTable.NewRow();
dr[0] = atomentry.Id.Uri.AbsoluteUri;
dr[1] = atomentry.Title.Content;
myTable.Rows.Add(dr);
}

dataGridView1.DataSource = myTable;

Great! Now you can see it on the screen (assuming you have a gridview called dataGridView1).

The next thing you want to do is click on an Account and return the details for that account.
That is where we want the:

_SingleResource = new SDataSingleResourceRequest(_sdataService);

so we initialize the sDataSingleResourceRequest with the sdataservice we created earlier and now we can tell it which account to go get and that we expect an account back.


_SingleResource.ResourceSelector = sId;
_SingleResource.ResourceKind = "Accounts";

var entry = _SingleResource.Read();
_SingleResource.Entry = entry;

From here the entry is xml and you can change it as you see fit.

This entire video and code sample was part of SalesLogix Bootcamp ’09 and the video is in the developers subscription.

What comes next? We will look at actually updating the records. The code was at bootcamp and you can check out the resources there. I am told the client libraries will be made to actually know that you are returning an “Account” entity back and be able to handle it a bit better in the next version. In this version we save about 50% of our time, so big steps!

Tags: , , , ,

Using ICriteria and Expression Factory with Disjunction

Posted by Diane Kohnert on September 02, 2009
Developer / No Comments

Sage.Platform.RepositoryHelper rep = Sage.Platform.EntityFactory.GetRepositoryHelper();
Sage.Platform.Repository.ICriteria criteria = rep.CreateCriteria();
criteria.Add(rep.EF.Disjunction()
.Add(rep.EF.Eq("Status","Active"))
.Add(rep.EF.Eq("Status","New"))
.Add(rep.EF.Eq("Status","Purge"))
);
criteria.AddOrder(rep.EF.Asc("Status"));
System.Collections.IList result = criteria.List();

After talking about using OR within a Criteria.Add and ExpressionFactory.OR, I started immediately thinking about adding multiple (or more than 2) into an where clause OR statement. I saw the Disjunction within the ExpressionFactory (seen here as EF within the RepositoryHelper) and decided to create a resultset using Disjunction.

In this case I am return all Accounts where Status = “Active” or Status = “New” or Status = “Purge”. I also put in an order by clause as seen with the
criteria.AddOrder(rep.EF.Asc("Status"));

The same can be done for multiple (more than 2) AND statements in your where clause using Conjunction. so the statement would be similar to the above but with Conjunction.
rep.EF.Conjunction()

Tags: , , , ,

Huber FSW – used at Boot Camp – Updated

Posted by Jason Huber on September 02, 2009
Administrator, Developer / No Comments

HuberFSW_Main

At Bootcamp we used a program called Huber FSW to watch for file changes on our image while we were running the application and performing common tasks in SalesLogix web. I created the tool after reading one of Diane’s examples in that book and wondering “is that really when that file is created?” – it was when that file was created and Huber FSW helped me see that. Initially I was going to write out all the files on the system to a text file using ls >> 1.txt via command prompt, run my changes and repeat ls >> 2.txt after I was done. Then compare 1.txt and 2.txt to see the changes.

I remembered when I was learning .NET that there was a class for watching files on the system called the FileSystemWatcher.

System.IO.FileSystemWatcher

Is the namespace, but really I just added one from the toolbox to my form and then set some parameters:

//do the file watching stuff
fsw.Path = txtFolderToWatch.Text;
fsw.IncludeSubdirectories = true;
fsw.EnableRaisingEvents = true;

then I handled each of the events it fires like Deleted and Changed:

this.fsw.Deleted += new System.IO.FileSystemEventHandler(this.fsw_Deleted);
this.fsw.Created += new System.IO.FileSystemEventHandler(this.fsw_Created);
this.fsw.Changed += new System.IO.FileSystemEventHandler(this.fsw_Changed);

and I was on my way. I worked a bit with the richtext box to get it to display anything with saleslogix in it or sage in it with colors:


if (sPathU.Contains("SAGE") || sPathU.Contains("SALESLOGIX") || sPathU.Contains("SLXCLIENT") || sPathU.Contains("SDATA"))
{
txtDisplay.SelectionBackColor = Color.Yellow;
if (sChangeType == "Deleted")
{
txtDisplay.SelectionColor = Color.Red;
}
else if (sChangeType == "Changed")
{
txtDisplay.SelectionColor = Color.Blue;
}
else if (sChangeType == "Created")
{
txtDisplay.SelectionColor = Color.Green;
}
logtext = "file:/" + logtext;
}

But that was about it.

Then a partner approached me and asked if I would make it save the files to another location. This posed some problems because the changes are actually handled in a callback to the form, so creating files and that sort of thing is a no-no, but I was able to make it happen.

On the main screen in this version you need to specify a folder to watch (the middle textbox). If you want to have the changes logged you can enter a file in the lower textbox. If you want the files copied to the folder where you are also logging you can do that by checking the lower checkbox. It cannot copy on deleted (the file has been deleted already).

Email me for the code, but the bits can be found here:
HuberFSW (zip)

Tags:

Using iCriteria Like ADO (and some HQL too)

Posted by Jason Huber on September 01, 2009
Developer / 2 Comments

The Old way

Most of us come from a vbscript background where we were writing classic ADO. Even if you started with .NET you are still familiar with the sort of code that we have here:

string sSQL = "select * from users where username = @username and password = @password";
 System.Data.SqlClient.SqlConnection conn =
 new System.Data.SqlClient.SqlConnection(System.Configuration.ConfigurationManager.
ConnectionStrings["Logins"].ToString());
 try
 {
 conn.Open();
 System.Data.SqlClient.SqlCommand comm = new System.Data.SqlClient.SqlCommand();
 comm.Connection = conn;
 comm.CommandText = sSQL;
 comm.Parameters.AddWithValue("@username", txtUserName.Text);
 comm.Parameters.AddWithValue("@password", txtPassword.Text);

 System.Data.SqlClient.SqlDataReader dr = comm.ExecuteReader();

 if (dr.HasRows)

//etc

right?

Now what?

Looking at SalesLogix web we now have nHibernate and what we are referring to as the iCriteria or iRepository structure for looking at our data through the entities that have been created for us.

How can we accomplish the sort of “select stuff from the database, do something with it” code that we know so well with ADO?

First, you can usually get to the entity you are “on” through a button click here:

public static void btnUpdate_OnClickStep1( IOpportunityDetails form,  EventArgs args)

using

Sage.Entity.Interfaces.IOpportunity myopp =
 (Sage.Entity.Interfaces.IOpportunity)form.CurrentEntity;

to cast it to the appropriate type (I am using Opportunities)

If you are in a business rule, the current entity is just passed to you:

public static void OnAfterInsert(IOpportunity opportunity)

Once you have the entity you can start working with the children of that entity.

I have a child entity in my latest HDI video called OpportunitySpread. I use the child to spread the Opportunity sales amount over a duration (another field I added to Opportunity)

The relationships are Opportunity.Opporunityspreads and OpportunitySpreads.Opportunity.

So how can I get all the spreads for the current opportunity (“select spreads from opportunity spreads where opportunity.id = opportunityspread.opportunityid” would be the sql equivalent).

//need to get the helper
 Sage.Platform.RepositoryHelper<IOpportunitySpread>
 myHelper = Sage.Platform.EntityFactory.GetRepositoryHelper<IOpportunitySpread>();

 //need to create my search criteria to filter the results
 Sage.Platform.Repository.ICriteria myCriteria = myHelper.CreateCriteria();
 //now I can say things like ID = oppid or whatever.
 myCriteria.Add(myHelper.EF.Eq("Opportunity", opp));
 System.Collections.IList listtoDelete = myCriteria.List();

Simple huh? The first line is creating the Repository “Helper” this object allows me to create an iCriteria object later.

You might remember iCriteria from Diane’s videos and post here:

http://slxtraining.net/2009/09/expressionfactory_or/

Once I have the iCriteria object I can add a variety of conditions onto it.

Things like:

EF.And
EF.Asc
EF.Between
EF.In

etc can all go into the

myCritieria.Add()

Then calling

myCriteria.List()

returns an iList of objects that matched my criteria.

These are actual objects that are the type that you passed in here:

//need to get the helper
 Sage.Platform.RepositoryHelper<IOpportunitySpread>
 myHelper = Sage.Platform.EntityFactory.GetRepositoryHelper<IOpportunitySpread>();

Videos related to this are:

“Compare Different Querying Methods: IRepository, NHibernate ICriteria, NHibernate HQL”

and

“Select Child Records for a Particular Parent ID”

Other resources on this subject are:

https://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html_single/#querycriteria (thanks to):

http://customerfx.com/pages/crmdeveloper/2008/05/22/creating-a-business-rule-to-return-a-list-of-objects.aspx

Tags: , , , , ,

Using AND with ICriteria

Posted by Diane Kohnert on September 01, 2009
Developer / No Comments


Sage.Platform.RepositoryHelper<Sage.Entity.Interfaces.IAccount> rep =
Sage.Platform.EntityFactory.GetRepositoryHelper<IAccount>();
Sage.Platform.Repository.ICriteria criteria = rep.CreateCriteria();
criteria.Add(rep.EF.And(rep.EF.Eq("Status","Active"),rep.EF.Eq("Type","Customer")));
System.Collections.IList result = criteria.List();

This example using an AND with ICriteria.  The two expressions are still sent in the resulting List would be different reflecting this Where clause equalivent:  Where Status = “Active” AND Type = “Customer”.

Tags:

Adding an OR Statement to ICriteria in Sage.Platform.Repository

Posted by Diane Kohnert on September 01, 2009
Developer / 1 Comment

Sage.Platform.RepositoryHelper<Sage.Entity.Interfaces.IAccount> rep = Sage.Platform.EntityFactory.GetRepositoryHelper<IAccount>();
Sage.Platform.Repository.ICriteria criteria = rep.CreateCriteria();
criteria.Add(rep.EF.Or(rep.EF.Eq("Status","Active"),rep.EF.Eq("Type","Customer")));

System.Collections.IList result = criteria.List();

In this example we will use the ExpressionFactory OR statement.  This example creates a List that has All Accounts where the Status = Active OR where the Type = Customer.  We will use Sage.Platform.Repository with ICriteria to create the query returning the results into a generic list.

In the code example below, the first line gets a repository for IAccount using the RepositoryHelper class.  We use the RepositoryHelper class because it creates the ExpressionFactory (seen as EF) and the ProjectionFactory (not seen but as PF) for us without additional lines of code that would exist if we used Repository instead of RepositoryHelper.  Using RepositoryHelper also lets us use CreateCriteria method directly from the repository (see line 2 below).

Now for the use of the OR in our criteria.  First we add a new criteria using criteria.Add method.  Since we have an OR condition we then use the repository’s Expression Factory to access the OR, then we place 2 expressions as parameters.   Seen below is rep.EF.Or with 2 expressions: the first expression is rep.EF.Eq with Status = “Active”, the next is rep.EF.Eq with Type = “Customer”.   It is easy to think of this in terms of what the result would look like in SQL:  Where Status = “Active” OR Type = “Customer”.

The final line brings back the result set from ICriteria into a generic List.

 Related Video Short! from the SalesLogix Developer’s Subscription:

Tags: , , , , , , ,