Introduction
In this article we want to add a simple extension to the project seen in the CodeProject article "A GPS tracer application for Windows Mobile CE 5". We want to create a Web Service which can be invoked by the mobile application to store the path followed by different GPS devices on a remote SQL2005 Database. The easiest and fastest way to achieve this result is represented by the Web Service Software Factory (WSSF for short). At the same time, this strategy is the best way to build higher quality service using well known patterns and practices.
Background
A GPS tracer application for Windows Mobile CE 5; Web Services Software Factory
Using the code
Our goal is incredibly simple: we just want to store an ordered collection of GPS coordinates (a path) sent by a mobile device. Obviously we desire to distinguish between different devices, and respective paths, which can send information contemporarily.
First of all, I created two tables in a database named GpsTracer
: the tables are Device
and LocationEntry
and are defined by the following SQL statements:
CREATE TABLE [dbo].[Device](
[ID] [uniqueidentifier] NOT NULL CONSTRAINT [DF_Device_ID] _
DEFAULT (newid()),
[CreationDate] [datetime] NOT NULL,
[LastLoginDate] [datetime] NOT NULL,
CONSTRAINT [PK_Device] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[LocationEntry](
[ID] [uniqueidentifier] NOT NULL CONSTRAINT [DF_LocationEntry_ID] _
DEFAULT (newid()),
[IDDevice] [uniqueidentifier] NOT NULL,
[Latitude] [float] NULL,
[Longitude] [float] NULL,
[WhenInserted] [datetime] NULL,
[Speed] [float] NULL,
[Quote] [float] NULL,
[Heading] [float] NULL,
CONSTRAINT [PK_LocationEntry] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]GO
ALTER TABLE [dbo].[LocationEntry] WITH CHECK ADD CONSTRAINT _
[FK_LocationEntry_Device] FOREIGN KEY([IDDevice])
This structure is quite simple but it's powerful enough for our purpose; we just want to collect the devices connected (identified by their Guid) and their first and last connection to the system (device
table); we want to know the paths they have ever followed (LocationEntry
table). Please note that the structure and tables of the database are the foundation of the solution built with the WSSF (and not only with that!) so we suggest thinking about them carefully before starting with the code generation. Of course, due to the nature of this "demo-project", we can go on.
After the database definition, the biggest part of the rough work is done by the WSSF. Once we create a new project of type "Web Service Software Factory (ASMX)" in Visual Studio, we should follow these steps (please refer to the Visual Studio Guidance Navigator window and it's "recipes" for more details; please remember to rebuild the solution between the steps):
- Connect the infrastructure to the database in the Service project: a connection string is added to the web.config file
- Create The CRUD Stored Procedure for the database tables in the Data Access Project: an SQL statement file is generated (see GpsTracerStoredCreation.sql)
- Create the Business Entities based on the database tables in the Business Entity project: the classes
Device
and LocationEntry
are created - Create the Data Repository Classes from Business Entities in the Data Access Project: the classes for connecting the entities and the database are created
- Create a Data Type accepted by the Web Service: the objects of this type will be the ones sent by the mobile application to the web server and they will contain all the information needed for the insertion of a "footstep" (a single step in the GPS path) in the database. In our case the type "
DeviceAndLocation
" contains the fields of both the database tables: deviceID
, deviceCreationDate
, deviceLastLoginDate
, locationEntryLatitude
, locationEntryLongitude
and so on - Create a Service Contract in the corresponding project: this is the signature of the Web Method that will be created. In our example it is named
SendDeviceAndLocation
, it returns a Boolean and has one input parameter of type DeviceAndLocation
Create the Service Contract Translators from the data type to the Business Entities. In our example this direction is enough because our application just receives data from the mobile devices. We define two translators: one from the data type to the device BE and one from the data type to the location BE - Add business logic to our solution: in our simple solution we add the class
DeviceAndLocationBLL
with the single method AcquireDeviceAndLocation
(see code described below) - Generate the Service Implementation: here we can finally implement our service (see code described below)
- Expose the service: the asmx file is generated
- Add web reference and implement a test in the windows form test project to check if insertion goes well!
Points of Interest
The few lines of code that we have to write are related to the method in the business logic and the service method. Let's start with the BLL.
This method checks if the device that sends information is already in the Device
table: if it is present then we update its last login date, else we insert it into the table.
It's peculiarity consists in the use of the GetAllFromDevice
(automatically created as Data Repository Classes from the CRUD stored procedures) the result of which is filtered with the find
method of a "generics list". The Predicate of the find
method is an anonymous delegate that checks if the ID of the input device parameter "fits" into an element of the list. Please note that the method is STATIC
: a class method is preferable for the performances and we should not mind atomicity of operations due to the use of the database.
public class DeviceLocationBll
{
public static bool AcquireDevice(Device inDev, LocationEntry inLoc)
{
DeviceRepository devRep = new DeviceRepository("GpsTracer");
List<device /> listDev = devRep.GetAllFromDevice();
Device devResult = listDev.Find(
new Predicate<device />(
delegate(Device devv)
{
return devv.ID == inDev.ID;
}
)
);
if (devResult == null)
{
devRep.Add(inDev);
}
Else
{
devResult.LastLoginDate = DateTime.Now();
}
LocationEntryRepository locRep = new LocationEntryRepository
("GpsTracer");
locRep.Add(inLoc);
return true;
}
}
The service implementation is quite easy: we just need to translate the object received from the web and pass the results to the AcquireDevice
method.
public bool SendDeviceAndLocationInfos
(GpsTracerServer.DataTypes.DevicePlusLocationType
SendDeviceAndLocationInfosRequest)
{
BusinessEntities.Device dev =
TranslateBetweenDevicePlusLocationTypeAndDevice.
TranslateDevicePlusLocationTypeToDevice
(SendDeviceAndLocationInfosRequest);
BusinessEntities.LocationEntry loc =
TranslateBetweenDevicePlusLocationTypeAndLocationEntry.
TranslateDevicePlusLocationTypeToLocationEntry
(SendDeviceAndLocationInfosRequest);
return DeviceLocationBll.AcquireDevice(dev,loc);
}
A very simple test implementation in the windows form is the following: we instantiate the proxy class which is needed to translate our DeviceAndLocation
to the corresponding soap packet.
private void ExecuteButton_Click(object sender, EventArgs e)
{
GpsTracerServiceContract ws = new GpsTracerServiceContract();
DeviceAndLocation testDev = new DeviceAndLocation ();
testDev.DeviceID = new Guid().ToString();
testDev.DeviceCreationDate = DateTime.Now;
testDev.DeviceLastLoginDate = DateTime.Now;
testDev.LocationHeading = 10;
testDev.LocationLatitude = 11;
testDev.LocationLongitude = 12;
testDev.LocationQuote = 13;
testDev.LocationSpeed = 14;
testDev.LocationWhenInserted = DateTime.Now;
ws.SendDeviceAndLocationInfos(testDev);
}
In this article, we used the Web Service Software Factory and the result is a simple web service based on a robust, extensible, well engineered foundation. We just emphasize that the entire solution can be built in 15 minutes and with a few lines of code.
History
Nothing to add... at this moment!