24
Sage SalesLogix 7.2 Web Client Programming FAQ Contents What key Composite UI Application Block (CAB) concepts am I likely to encounter when writing customization code for Sage SalesLogix Web?.....................................2 ObjectBuilder.......................................... 2 Modules................................................ 2 WorkItems.............................................. 2 Workspaces............................................. 2 Smart Parts............................................ 2 Services............................................... 2 What Services are provided with the Sage SalesLogix Portal?..................................................2 How do I access the Page WorkItem from my Smart Part?....2 How do I get just the UserID of the currently logged on user?....................................................2 How do I access the User entity for the currently logged in user?.................................................2 How do I get the database connection string?.............2 How do I access the Entity that my quick form is bound to? .........................................................2 My quick form is bound to a child entity of Contact, and is displayed in the tab workspace of the Contact page. How do I get the current Contact?............................2 How do I pass basic state (name/value) information between quick forms?.............................................2 On a quick form, how do I detect a change in one control, and make a change to another control?....................2 How do I access/control the tab workspace at runtime?....2 How do I show a MessageBox from code in my Smart Part?...2 How do I determine if a property has been changed since the last save?...........................................2 How do I create and work with an entity instance in code? 2 How do I query for an entity given a known ID?...........2 How do I create a criteria-based query for an entity, similar to a “select-from-where” SQL query?..............2 Page 1

SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

Sage SalesLogix 7.2 Web Client Programming FAQ

Contents

What key Composite UI Application Block (CAB) concepts am I likely to encounter when writing customization code for Sage SalesLogix Web?........................................2

ObjectBuilder...............................................................................................................2Modules.......................................................................................................................2WorkItems...................................................................................................................2Workspaces..................................................................................................................2Smart Parts...................................................................................................................2Services........................................................................................................................2

What Services are provided with the Sage SalesLogix Portal?.......................................2How do I access the Page WorkItem from my Smart Part?............................................2How do I get just the UserID of the currently logged on user?.......................................2How do I access the User entity for the currently logged in user?..................................2How do I get the database connection string?.................................................................2How do I access the Entity that my quick form is bound to?..........................................2My quick form is bound to a child entity of Contact, and is displayed in the tab workspace of the Contact page. How do I get the current Contact?................................2How do I pass basic state (name/value) information between quick forms?..................2On a quick form, how do I detect a change in one control, and make a change to another control?...............................................................................................................2How do I access/control the tab workspace at runtime?..................................................2How do I show a MessageBox from code in my Smart Part?.........................................2How do I determine if a property has been changed since the last save?........................2How do I create and work with an entity instance in code?............................................2How do I query for an entity given a known ID?............................................................2How do I create a criteria-based query for an entity, similar to a “select-from-where” SQL query?......................................................................................................................2How do I combine a series of criteria query expressions with the AND or OR operator?.........................................................................................................................................2What if I want to query for an aggregate value such as count, sum, average, etc?.........2How do I make criteria comparison case-insensitive?....................................................2When setting a date/time property on an Entity, what time zone should I use?..............2How do I get the time zone of the client (web browser)?................................................2How do I access a user option?........................................................................................2Why do I get build errors when attempting to implement some of the examples in this FAQ?................................................................................................................................2

Page 1

Page 2: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

What key Composite UI Application Block (CAB) concepts am I likely to encounter when writing customization code for Sage SalesLogix Web?

CAB is an application block created by the Microsoft Patterns & Practices team, based on real-world professional services experiences in implementing enterprise business applications. This open source application block provides support for implementing many common and proven software patterns. The most visible patterns supported are the Model View Controller (MVC) pattern, the Plugin pattern (for module loading), the Service Locator pattern, and the Dependency Injection pattern. Additional patterns supported include Builder, Command, and Pub/Sub.

These patterns are combined in a manner that supports the development of multiple, discrete parts that work together when plugged into a host shell application. This corresponds with the Sage SalesLogix customization and deployment strategy.

CAB was created with a bias towards WinForms-based clients. The new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on other CAB components are used to implement the other patterns in a platform-independent manner, with our first implementation being used to support the new Sage SalesLogix Web Client.

You can read more about the high-level concepts of CAB here: http://msdn2.microsoft.com/en-us/library/aa546409.aspx

ObjectBuilder

ObjectBuilder serves as the backbone of the Sage SalesLogix application architecture by providing a central framework for the creation, initialization, and management of objects in the application domain. ObjectBuilder is made up of the following parts:

Locator – The locator is the central repository for all objects that pass through object builder. As its name implies, the locator is used to find objects (such as services).

Lifetime Container – Ensures that an object remains alive (not garbage collected) for the appropriate lifecycle required by the application.

Builder Strategies – A configurable set of IBuilderStrategy instances through which each object is passed, allowing particular objects to be constructed or initialized in a specific way.

Dependency Injection Attributes – Attributes that define how an object will be constructed and\or initialized.

Page 2

Page 3: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

In CAB, a developer would rarely interact directly with ObjectBuilder, since most of its functionality is wrapped by the CAB WorkItem (see below). However, there may be times when a developer needs more explicit control over how a particular object is built up. Here are just a couple of examples:

Executing Code during Build-Up

public class MyObject{ [Microsoft.Practices.ObjectBuilder.InjectionMethod] public void InitializeMyObject() { //Do something within my object during build up }}

Controlling Which Constructor is used by ObjectBuilder

public class MyObject{ /// <summary> /// This constructor is used when constructed through ObjectBuilder /// </summary> [Microsoft.Practices.ObjectBuilder.InjectionConstructor] public MyObject() { }

/// <summary> /// This constructor is used when it is explicitly called in code /// </summary> /// <param name="someParameter"></param> public MyObject(string someParameter) {

}}

Note: If you add an InjectionConstructor attribute to one of your classes, it will only be used if your class is instantiated through one of the managed collections for a WorkItem (such as Items or Services). See the WorkItems section below for more information on this. Also, do not use constructor injection on anything inherited from Control in the Web (such as ASP.NET SmartParts). Smart Parts are created with their default constructor, then passed into the WorkItem that contains them via the Items.Add() method.

Modules

In CAB, a module typically contains a collection of related WorkItems. However, in the Sage SalesLogix Web environment, “empty” (no WorkItems) modules are often used to control UI and/or register services to a Page WorkItem during the module load event.

Page 3

Page 4: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

In the Application Architect, you can map modules to a Portal Page on the Modules tab of the page editor. If you create your own module, it should derive from IModule, located in Sage.Platform.Application:

namespace Sage.Platform.Application{ public interface IModule { WorkItem ModuleWorkItem { get; }

void Load(); }}

WorkItems

A WorkItem represents a use case, or unit of work, in an application. It is a container for UI elements and views, state, and services. Every CAB application contains a Root WorkItem that serves as the top level container for global services and the other WorkItems that make up the application. You can find the definition of the WorkItem class in the Sage.Platform.Application assembly. Also in that assembly, but under the Sage.Platform.Application.UI namespace is the UIWorkItem class. This class adds a collection of UI Extension Sites, which are things such as Menus and Toolbars that can have children appended to them, and command handlers associated with the OnClick events.

Sage SalesLogix provides an Application Context object in the Sage.Platform.Application assembly that exposes the Root WorkItem through a property named “Current”:Sage.Platform.Application.ApplicationContext.Current

Each Portal Page in a Sage SalesLogix Web portal application is contained by a page-level WorkItem. If you write a custom module and associate it with a page, you can access the Page WorkItem through dependency injection:

public class MyCustomModule : IModule{

private UIWorkItem _parentWorkitem;

[ServiceDependency(Type=typeof(WorkItem))]public UIWorkItem ParentWorkItem{

get { return _parentWorkItem; } set { _parentWorkItem = value; }

}

…The rest of the class implementation…}

Note: By “associate it with a page” in the previous paragraph, we are referring to instantiating your class via the WorkItem’s Items.AddNew() or Services.AddNew() methods to instantiate the class; by using the Items.Add() or Services.Add() methods with

Page 4

Page 5: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

an existing instance of your class; or through the WorkSpace Show() method. Module, Service and SmartPart classes that are mapped into a Portal in the Application Architect will automatically be created this way. For additional classes that you define and instantiate within your modules, Services or Smart Parts, you will need to use the Add/Show methods yourself if you want to take advantage of dependency injection.

Accessing the WorkItem type through the ServiceDependency attribute will result in your property being associated with the closest WorkItem in the chain from the module in which your code resides. For Services associated to a Portal, that will be the Root WorkItem. For Smart Parts and modules associated to a Portal Page, it will be either the Page or Workspace WorkItem.

Workspaces

Workspaces are UI containers for Smart Parts. The Sage SalesLogix portal page template contains MainContent, Tab, Dialog, ToolBar, and NavigationBar workspaces. By separating Smart Parts from their WorkSpace containers, the look of an application can be changed through configuration. In the Application Architect, when you map a Smart Part to a page, you can configure which WorkSpace that Smart Part will be associated with.

The Workspaces provided for the Sage SalesLogix Web Client have their own WorkItems. When a Portal Page initializes the Smart Parts that are mapped to that page, they add the Smart Parts to CAB via the Workspace Show() method, which associates the Smart Parts with the Workspace WorkItem. The reason for this additional WorkItem below the Page WorkItem is so we can have separate entity contexts between Smart Parts on the same page.

Smart Parts

Smart Parts are UI user controls that are loaded into Workspaces to make up the UI of an application. In Sage SalesLogix, quick forms start out as metadata used to describe a form layout, and are then turned into Smart Parts when you build the client platform. When you build the Web platform, quick forms are turned into .NET 2.0 Web forms with an ASCX extension. You can also create custom controls outside of the Sage SalesLogix development environment and load them into the provided Workspaces as Smart Parts.

Services

Services are objects contained by WorkItems that are available for use by many different components. Global services are registered with the Root WorkItem services collection. Examples of types of services include security services such as authentication, state persistence, and tracing. Sage SalesLogix provides a number of Sage SalesLogix-specific services that will be described later in the document.

Page 5

Page 6: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

CAB provides additional value when working with services via ObjectBuilder and the ability to use “Service Injection” to dynamically resolve service dependencies. Let’s say a module or WorkItem needs to access a service to find out information about the currently logged on user. With CAB, you might be aware that a particular application provides an implementation of a service based on the IUserService interface, but do not know anything about the implementation itself. Fortunately, you do not need to know about the implementation details, or what framework calls you need to make to access the service. With Service Injection, you just declare one of your class properties to have a dependency on the IUserService interface, and the application (through ObjectBuilder) will take care of associating your property with the current implementation instance for that service:

public class MyCustomModule : IModule { private IUserService _userService; [ServiceDependency] public IUserService UserService { get { return _userService; } set { _userService = value; } }

…rest of the class… }

You can add custom services to a Portal in the Application Architect one of two ways. 1. Add a global service by registering your class on the Advanced tab of the portal

editor. 2. If you want to add a service that is available only within a particular Page

WorkItem, you can add a custom module on the Modules tab for a Portal Page editor, and then add your service to the Page WorkItem’s services collection.

What Services are provided with the Sage SalesLogix Portal?

SalesLogix Web User Service [IUserService]: Provides access to information about the currently logged in user.

Group Context Service [IGroupContextService]: Find out what the currently active group is, the default group for the currently active entity, and manipulate group caching settings.

User Options Service [IUserOptionsService]: Allows getting/setting of Sage SalesLogix user options.

SalesLogix Data Service [IDataService]: Provides access to the database connection string.

Entity Context Service [IEntityContextService]: This service is loaded at the Portal Page level. Track entity context (current entity on a main view), track changes, and access entity history.

Page 6

Page 7: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

Client Context Service [ClientContextService]: This service is loaded at the Portal Page level. It is a basic name/value dictionary. What separates this service from just using Page WorkItem State is that the Sage SalesLogix Web Client provides client-side access to the dictionary at the page level. You can access it on the client in JavaScript via Sage.Services.getService(“ClientContextService”).

Page WorkItem Locator Service [IPageWorkItemLocator]: Use this service from a Smart Part to locate the Page WorkItem container. This service is provided because a Smart Part might have other WorkItems between itself and the Page WorkItem. For example, the Workspaces in the provided Sage SalesLogix master pages have their own WorkItems that contain the Smart Parts.

How do I access the Page WorkItem from my Smart Part?

Quick forms have a PageWorkItem property you can use. For a custom Smart Part, use the Page WorkItem Locator service:

using Sage.Platform.Application.UI.Web;

public class MyModule : IModule{ private IPageWorkItemLocator _pageWorkItemLocator; [ServiceDependency] public IPageWorkItemLocator PageWorkItemLocator { get { return _pageWorkItemLocator; } set { _pageWorkItemLocator = value; } } …

Or:

IPageWorkItemLocator locatorService = Sage.Platform.Application.ApplicationContext.Current.Services.Get<IPageWorkItemLocator>();

How do I get just the UserID of the currently logged on user?

To get the UserID, access the currently active implementation of IUserService. In the Web environment, this is provided through the SlxWebUserService class. This service is associated with the Root WorkItem.

Here is an example of how you can access this service in a business rule or module:

Sage.Platform.Security.IUserService userService = Sage.Platform.Application.ApplicationContext.Current.Services.Get<Sage.Platform.Security.IUserService>();

string currentUserId = userService.UserId;

Page 7

Page 8: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

The previous example is querying for the service object from the Services collection in the Root WorkItem. This is the approach you need to take when accessing services from a Business Rule, as the business rule classes are not part of the WorkItem.Items collection. If you were creating your own Module class, you might choose to use Service Injection (this example assumes you’ve added Sage.Platform.Application and Sage.Platform.Security to your “using” section:

private Sage.Platform.Security.IUserService _SlxUserService;

[ServiceDependency]public Sage.Platform.Security.IUserService SlxUserService{ set { _SlxUserService = value; } get { return _SlxUserService; }}

By doing it this way, our class does not need to know about the ApplicationContext object that Sage SalesLogix provides. It only needs to know about the IUserService interface. This is a powerful concept in a “composite” application that might include developers with varying levels of familiarity with the underlying framework.

Note: In order to use dependency injection as previously explained, your class needs to be “associated” with the CAB infrastructure (as described in the previous WorkItems section). Also, please be careful when using attribution this way. If this service will be used every time your class is used, you are fine. However, if you have a complex module with multiple use cases, and you will not always need to access the User Service (in this example), then you might want to dynamically set up the variable.

How do I access the User entity for the currently logged in user?

The Sage SalesLogix implementation of IUserService exposes a GetUser() method that returns a User entity as defined in the Sage SalesLogix Security Support entity package in the Application Architect. When you request the IUserService from the Root WorkItem, you need to cast it to the SLXUserService type:

Sage.SalesLogix.Security.SLXUserService userService = Sage.Platform.Application.ApplicationContext.Current.Services.Get<Sage.Platform.Security.IUserService>() as Sage.SalesLogix.Security.SLXUserService;

IUser user = userService.GetUser();

How do I get the database connection string?

While we encourage programmers to do as much as possible through the Entity model, in some cases it is necessary to revert to making ADO calls. You can access the current

Page 8

Page 9: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

connection string through the Data Service, which is exposed through the IDataService interface:

Sage.Platform.Data.IDataService service = Sage.Platform.Application.ApplicationContext.Current.Services.Get<Sage.Platform.Data.IDataService>();

String connection = service.GetConnectionString();

How do I access the Entity that my quick form is bound to?

In the Web platform, the user control that is generated from the quick form metadata is derived from the Sage.Platform.WebPortal.SmartParts.EntityBoundSmartPartInfoProvider class. This class contains a public read-only property named BindingSource that represents the primary binding source for the form. This BindingSource object contains a “Current” property that returns an object that can be cast to the appropriate entity type. Here is an example of a code snippet on an Account form that accesses the Account entity through the BindingSource property:

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

Owner.Enabled = account.CanChangeOwner();

My quick form is bound to a child entity of Contact, and is displayed in the tab workspace of the Contact page. How do I get the current Contact?

There are two ways to do this. One is to add a Data Source to your form and point it to the parent entity. You can then bind to this Data Source, or access it at runtime in code. The other way is to use the GetParentEntity() method of the EntityBoundSmartPartInfoProvider class, from which the Quick Form Web control is inherited.

To point a Data Source to the parent entity, first set the EntityTypeName property. In this case, you would set it to Contact. Next, set the GetByMethod to “<getParent>” from the drop-down list. This is an example of using this Data Source (assuming we name the Data Source control dsContact):

Sage.Entity.Interfaces.IContact parentContact = dsContact.Current as Sage.Entity.Interfaces.IContact;

The following is an example using the GetParentEntity() method:

Sage.Entity.Interfaces.IContact parentContact = GetParentEntity() as Sage.Entity.Interfaces.IContact;

Page 9

Page 10: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

How do I pass basic state (name/value) information between quick forms?

The class that quick forms are derived from contains a PageWorkItem property that you can use to access the Page WorkItem. All WorkItems contain a State collection that you can use to set and retrieve state information at runtime. The Page WorkItem state is special in Sage SalesLogix Web Client, in that we persist it between post backs.

Here is a code example of how you would do this in a code snippet action on a quick form:

this.PageWorkItem.State[“SomePieceOfInformation”] = “Something Interesting”;

On another quick form on the same page (perhaps a dialog that is launched from the first quick form), you could access this information like so:

this.LabelControl.Text = this.PageWorkItem.State[“SomePieceOfInformation”].ToString();

this.PageWorkItem.State.Remove(“SomePieceOfInformation”);

See the question “How do I access the Page WorkItem from my Smart Part?” for how to access the Page WorkItem from a custom (not a quick form) control.

On a quick form, how do I detect a change in one control, and make a change to another control?

First, make sure that, as much as possible, you are doing business logic at the entity level. For example, if setting Property A to a certain value should result in a change to Property B, it would be desirable to have that logic in an event handler for Property A. This way, you do not have to remember to include this logic every time you create a UI part that exposes Property A.

One problem with this approach in the Web UI is that the Entity object is not instantiated on the client-side, so a change to Property A through a control will not affect Property B until a Postback takes place. To deal with this, you can create an OnChange action for the control that is bound to Property A. Any OnChange action will result in a Postback in the Web Client. If you do not have any specific actions to perform, other than that you want the control that is bound to Property B to be updated, the appropriate OnChange action to use would be “Refresh Data”. In the Web Client, this will force a Postback of the current Smart Part.

Another Action type that is useful for manipulating UI controls is the “User Interface interaction” action. This action allows you to specify a ControlID that you want to modify, as well as a Property Name and Value. It might be tempting to use this Action type with the OnChange for a control on a form, but beware that in some cases, another postback by a separate control might cause your change to be lost. The safest way to use

Page 10

Page 11: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

the User Interface interaction is to create Form OnLoad actions and set the “On Repaint Event” property for these actions to True. Setting the On Repaint Event property will cause the action to be run on every postback, as opposed to just on the initial form load event.

Finally, you can just create C# Snippet actions and use them in the same way described previously for the User Interface interaction.

How do I access/control the tab workspace at runtime?

Like all Web workpsaces, the Tab workspace is a Web Server Control and can be controlled through code.  You can access it through the CAB infrastructure:  this.PageWorkItem.Workspaces["TabControl"];  (Note that "TabControl" is the ID of the workspace in the markup of the masterpage.)  It can also be found by looking through the Controls collection of the Page.

The API reference contains all of the properties and methods, but here are a few that might be interesting to a developer:

Show() - use this method to add Smart Parts (tabs) to the workspace. Hide() - use this method to remove a Smart Part from the workspace. IncludeMiddlePane - use this boolean  property to include a "middle pane" area in

the page for the user to display a tab in. IncludeMoreTabsTab - use this boolean property to include a "More Tabs" tab. DefaultVisibleTabCount - use this property to set the number of tabs that will be

included in the tabstrip. The rest of the tabs will be included in the "More Tabs" tab.  Note: this property only applies if IncludeMoreTabsTab is set to true.

OverrideActiveTab - use this property to programatically open a specific tab. Set the value of this property to the ID of the Smart Part you want displayed.  Make sure this is set early in the page lifecycle, such as Page_Load.

There are also several style related properties to control the visual display of the tabs.

How do I show a MessageBox from code in my Smart Part?

Quick forms are derived from EntityBoundSmartPart, which exposes a DialogService property that you can use to display messages in the dialog workspace. Here’s an example of how you can use this property from a snippet action:

this.DialogService.ShowMessage("Hello World");

You can use dependency injection to get a reference to the dialog service from a custom Smart Part: private IWebDialogService _DialogService;

[ServiceDependency] public IWebDialogService DialogService {

Page 11

Page 12: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

set { _DialogService = value; } get { return _DialogService; } }

How do I determine if a property has been changed since the last save?

Sage SalesLogix entities implement the IEntityState interface, which provides access to change state information:

Sage.SalesLogix.Orm.IEntityState accountState = account as Sage.SalesLogix.Orm.IEntityState;

IDictionary<string, ComponentChange> changeState = accountState.GetChanges(); string matchingValue;if (changeState.ContainsKey("MainPhone")){ //This inspects the workPhone of all the account contacts. matchingValue = (string)changeState["MainPhone"].OldValue;}

How do I create and work with an entity instance in code?

Your business rules are always passed through the current instance of the Entity for which the business rule was created. However, what if you want to dynamically create an instance of another entity? If you look at the code we generate when you build a Web platform (in c:\documents and settings\<Current User>\Application Data\Sage\Platform\Output), you may notice that in addition to generating the Interfaces DLL, we also generate a Sage.SalesLogix.Entities project and DLL that provides the implementation for the entity interfaces. It is fine to use this project for debugging purposes, but you should not reference this DLL directly in your code. This DLL is tied to the Web platform, and you want your code to remain platform-independent so that it is portable to different client types (Web Client, Web Service, WinForms Client, etc.).

So if you cannot access the implementation directly, how do you new up and work with an entity instance? Sage SalesLogix provides the EntityFactory static class (in Sage.Platform.dll) for this purpose. The Entity Factory serves as the single entry point for creating, deleting, and querying for entities in code, and it separates your code from the actual entity implementation.

Page 12

Page 13: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

Actually, the EntityFactory itself is just serving as a gateway to the true wrapper around our entity implementation, the IRepository interface and corresponding implementation. The purpose of the Repository class is to provide an abstraction from the underlying NHibernate implementation, and simplify certain tasks. The EntityFactory contains a collection of repositories, one for each entity type. Both of these classes make extensive use of .NET 2.0 generics.

Code example of creating an entity instance:

account.Address = Sage.Platform.EntityFactory.Create<IAddress>();

Underneath the covers, the EntityFactory Create method locates the repository for the Address entity and calls that repository’s create method.

How do I query for an entity given a known ID?

The EntityFactory exposes the GetByID method for simple query by ID situations. Like the Create method, this is simply a wrapper around the underlying repository method:

Sage.Entity.Interfaces.IContact contact = Sage.Platform.EntityFactory.GetById< Sage.Entity.Interfaces.IContact>("CXYZ00000001");

How do I create a criteria-based query for an entity, similar to a “select-from-where” SQL query?

The IRepository interface is abstracting the developer from the underlying NHibernate implementation. However, we do still use the NHibernate HQL query language. Examples of NHibernate queries can be found online here: http://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html_single/#querycriteria.

In order to perform queries, there is a small amount of setup that needs to be done. The query functionality is exposed by the repository through a separate IQueryable interface. The IExpressionFactory interface provides access to factory methods for obtaining certain built-in simple expressions, such as Like, Or, And, Eq, and IsNull:

// Setup (you could cache this somewhere in your class)Sage.Platform.Repository.IRepository<IContact> rep = EntityFactory.GetRepository<IContact>();

Sage.Platform.Repository.IQueryable qry = (IQueryable)rep;

Sage.Platform.Repository.IExpressionFactory ef = qry.GetExpressionFactory();

Note that in NHibernate, there is a static “Expression” class for the Expression Factory stuff. The reason we do not expose this as a static class in Sage SalesLogix is we [will] support multiple repository types. In the future, you are likely to see integration scenarios where a new Entity Package is added to the Project Explorer that works with a

Page 13

Page 14: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

different repository, perhaps one that reads and writes from another database (such as in a front office/back office integration scenario).

Now that we have set up our IQueryable and IExpressionFactory variables, here is an example of a basic criteria query. In this query, we want to return a list of primary contacts. The CreateCriteria method creates a new criteria query and returns it using the ICriteria interface. We then add the query condition (IsPrimary == True) using the Add method of ICriteria. The Add method also returns a criteria query. This allows us to string our query together using dot notation in a very readable format:

IList<IContact> contactList = qry.CreateCriteria() .Add(ef.Eq("IsPrimary", True)) .List<IContact>();

Here is an example that adds an alias for referencing an associated 1:1 entity in the ordering clause:

IList<IUser> results = qry.CreateCriteria() .Add(ef.In("Id", users.ToArray())) .CreateAlias("UserInfo", "ui") .AddOrder(ef.Asc("ui.LastName")) .List<IUser>();

How do I combine a series of criteria query expressions with the AND or OR operator?By default, calling ICriteria.Add(IExpression) successively will combine all expressions with the AND junction.  If you want to combine an arbitrary number of expressions with OR, you can use the IJunction interface, as follows:

Sage.Platform.Repository.IJunction junction;

if (weWantToAndThisExpression){    junction = ef.Conjunction(); // AND}else{    junction = ef.Disjunction(); // OR}

ICriteria.Add(junction .Add(ef.Eq("Account", account)) .Add(ef.Eq("IsPrimary", true))) .List<IContact>();

What if I want to query for an aggregate value such as count, sum, average, etc?

Page 14

Page 15: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

Just as we used the expression factory to access operators such as In, Ascending, Less Than, etc., we have a “projection” factory that exposes the aggregate functions (or projections). The factory is represented by the IProjections interface, which contains a collection of IProjection instances. You apply a projection to a query by calling SetProjection().

Here, we will use a projection on a Contact query to count how many contacts are marked as primary for a particular account:

int result = qry.CreateCriteria() .Add(ef.Eq("Account", account)) .Add(ef.Eq("IsPrimary", true)) .SetProjection(proj.Count(“Id”)) .UniqueResult<int>();

Note that we also introduced “UniqueResult”. In this case, because we only had a single projection that was returning an integer, we could tell the criteria query to return a single unique result, rather than an object array.

How do I make criteria comparison case-insensitive?

Use the IgnoreCase member of the simple expression (ISimpleExpression) that you are adding to the criteria query. In this case, we are adding IgnoreCase to the simple expression returned by “Eq”:

IList<IContact> contacts = qry.CreateCriteria() .Add(ef.Eq("Account", contact.Account).IgnoreCase) .List();

When setting a date/time property on an Entity, what time zone should I use?

When writing business rules, all date/time operations should be done in UTC time. While the application is going through the Sage SalesLogix provider, within the confines of the new Web application framework, date/time conversion is turned off. This was done so that business rules can run consistently in multiple types of client applications (Web, Windows, mobile):

DateTime someDateTimeField = DateTime.UtcNow;

How do I get the time zone of the client (web browser)?

One of the global services provided by Sage SalesLogix is the Context Service (IContextService), which supports saving and retrieving name/value pairs. When logging on to the application, the TimeZone of the client is stored in the Context Service with a key of “TimeZone”:

Page 15

Page 16: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

Sage.Platform.Application.IContextService context = Sage.Platform.Application.ApplicationContext.Current.Services.Get<IContextService>(true);if (context.HasContext("TimeZone")){ Sage.Platform.TimeZone tz = (Sage.Platform.TimeZone)context.GetContext("TimeZone");}

How do I access a user option?

Sage SalesLogix provides a User Options service that is available through the Root WorkItem:

Sage.SalesLogix.IUserOptionsService userOption = ApplicationContext.Current.Services.Get<IUserOptionsService>();

opportunity.Status = userOption.GetCommonOption("pklStatus", "OpportunityDefaults");

Why do I get build errors when attempting to implement some of the examples in this FAQ?

Sage SalesLogix uses code generation to convert business rule code snippets into binary assemblies for use in the web client. In order to build snippet libraries, the .NET compiler needs to be able to find any assemblies referenced in snippet code. If it can’t find a referenced assembly, you’ll get an error like this:

ERROR - %path%\codesnippet.cs(33,18): The type or namespace name 'SalesLogix' does not exist in the namespace 'Sage' (are you missing an assembly reference?)

The Application Architect uses an XML configuration file to track assembly references:

C:\Documents and Settings\All Users\Application Data\Sage\Platform\Configuration\Global\codesnippets.xml

The “defaultReferences” section contains the assembly reference list. Out of the box, the list includes Sage.Platform, Sage.Platform.Application, Microsoft.Practices.ObjectBuilder, Sage.Platform.Configuration, and Sage.Entity.Interfaces. If you reference any other assemblies, you’ll need to add entries for those in the defaultReferences section. You’ll see by the namespace prefixes that some of the examples in this FAQ reference assemblies not in this list.

Note: A future release of the Sage SalesLogix Application Architect will include UI for managing assembly references in your application, so modification of codesnippets.dll should just be considered a temporary workaround. Also, codesnippets.xml is generated automatically if it doesn’t exist, so you can delete the file to return to the original configuration.

Page 16

Page 17: SalesLogix 7 - GeoffLogix · Web viewThe new Sage SalesLogix Web Client architecture uses ObjectBuilder to implement the Builder and Dependency Injection patterns. Classes based on

Another error you might encounter in the gold release of SalesLogix 7.2 is related to doing a “full” build of the web platform (holding down the <ctrl> key while clicking “Build Web Platform”:

ERROR - %path%\codesnippet.cs(29,41):The type or namespace name 'I<YourEntityName>' could not be found (are you missing a using directive or an assembly reference?)

In this case, <YourEntityName> would be the name of a custom entity you have created and are referencing in a code snippet. This is a defect in the gold release where a full build uses the Sage.Entity.Interfaces.dll that is installed with the product, rather than the modified version of that assembly (which includes your customizations) in the output directory. We’ve fixed this issue for the Service Pack 1 release. In the mean time, you can work around the problem by either doing a regular build (not holding down the <ctrl> key), or just building the code snippets, after you get the above error.

Page 17