Chapter 12. Dissecting a LightSwitch Application

Visual Studio LightSwitch is a powerful environment that makes it easier for developers to build modern, high-quality business applications. One great benefit is that you use the same approach even with different data sources, ignoring what happens behind the scenes, and you use the same screen templates regardless of the data source that will be bound to the user interface.

To provide this great level of flexibility and abstraction, the LightSwitch team has worked very hard. After all, it was necessary to take different .NET technologies and integrate them to build an architecture that allows three tiers in an application to be abstract from one another but also to communicate with one another in an efficient way. So, you have an easy way to build applications, but also a robust application infrastructure based on complex architecture.

This chapter explains how LightSwitch applications are architected and takes a close look at the three tiers. In addition, you learn how a LightSwitch project is actually handled by Visual Studio, which is not the simplified, logical view that you get in Solution Explorer. Note that this chapter requires some previous experience in building client/server applications; this prerequisite will make it easier to understand several concepts related to the architecture implementation. In addition, some sections assume that you have some previous experience with SQL Server. This chapter introduces you to concepts that are required later in this book and gives you a better understanding about why you performed some previous tasks.

Applications Architecture and Tiers

One great benefit of LightSwitch is the application’s architecture.

Although you will not usually see what happens behind the scenes because you focus on your business logic, the Visual Studio LightSwitch team has provided a robust, maintainable, clean infrastructure for high-quality, powerful applications built with this new product. Basically, LightSwitch applications are built upon a typical 3-tier architecture. If you are not new to development and have some experience building data-centric applications, you know how complex and difficult it can be to build maintainable 3-tier applications, because of the huge number of design and other option issues. Visual Studio LightSwitch solves this problem for you, as it is responsible only for the architecture, thus allowing the developer to focus on writing the business logic. In the LightSwitch architecture model, applications rely on the following tiers:

Data storage: It consists of the sources that expose data, including databases, services, and list feeds.

Logic tier: This tier is responsible for data interaction between the client and the storage, including queries, updates, and data validation. It is also responsible for protecting the storage from invalid data operations.

Presentation tier: This tier provides the interaction with the end users via a graphical user interface whose main goal is displaying data and allowing information to be changed.

These tiers are not specific to LightSwitch. Instead, they are logical representations of some architecture requirements that exist in most data-centric applications; every tier must be implemented by adopting one or more of the technologies specific to the scenario for which the application is created. For example, the data storage tier is represented by a database engine. The following subsection provides an architecture overview based on technologies. After that, you learn detailed information about all tiers in the architecture.

Architecture Overview

Visual Studio LightSwitch simplifies things by adopting the most recent technologies from Microsoft for each tier in the architecture. Figure 12.1 shows the LightSwitch application architecture, annotated with relevant technology.

Image

Figure 12.1. LightSwitch application architecture (and technology relevant at each technology tier).

As you can see, multiple technologies are used in every tier. Here is a description of the technologies and tiers:

Data storage: Applications can store information in SQL Server (including the Express Edition) and SQL Azure relational databases or in SharePoint 2010 lists. In addition, data sources that have an Entity Framework 4 provider are supported.

Logic tier: This tier provides runtime infrastructure based on the ASP.NET 4.0 engine, which is necessary for running applications built upon Silverlight. Next, the logic tier is responsible for data access, and it uses the ADO.NET Entity Framework as the modeling platform, plus WCF RIA Services.

Presentation tier: This layer in the application provides the user interface, enabling users to display and edit data via appropriate controls and interactions.

Each tier plays a fundamental role in the application, and understanding how tiers work is important to leverage all the best features and performances in your LightSwitch applications. For this reason, this chapter explains in detail how applications are architected and how projects are organized from a Visual Studio perspective.

Understanding the Data Access and Storage Tiers

The data storage is where information is saved, and it is typically represented by a relational database or a structured file, such as XML. Visual Studio LightSwitch supports direct access to SQL Server, SQL Azure, and SharePoint 2010 as data storages. Actually, you can also take advantage of WCF RIA Services as bridges to other storage.

In the LightSwitch terminology, you often hear data storage also as data source. For every supported data storage, LightSwitch has an appropriate data provider based on the .NET Framework. Data providers are explained in the next subsection.

Understanding Data Providers: SQL Server and SQL Azure

Data providers are basically .NET components allowing interaction between managed code and the data storage, so they are the first level in the architecture after the data storage. In LightSwitch, there are two top-level data providers, one for Microsoft SQL Server and SQL Azure and one for Microsoft SharePoint 2010. The provider for SQL Server and SQL Azure is called SQL Client for the Entity Framework and is used by the ADO.NET Entity Framework modeling engine to map database objects into .NET classes, in addition to establishing connections, managing connection strings, and handling transactions. Because in LightSwitch you can use different versions of Microsoft SQL Server as the data store (2005, 2008, and 2008 R2), the SQL Client provider cannot support the same features in every version. Table 12.1 summarizes supported and unsupported features in the SQL Client provider, grouped by SQL Server version (including Express Editions).

Table 12.1. Available and Unavailable Features in SQL Client for SQL Server

Image

As you can see, the SQL Client data provider does not support stored procedures, but this only happens in LightSwitch. In classic .NET 4 applications, it does support stored procedures. As a common rule, in LightSwitch the SQL Client data provider also does not support accessing SQL Server Compact Edition, but it supports referential integrity in each version. The SQL Client data provider is also used against SQL Azure. Due to the nature of this relational database engine, the following limitations apply:

• Windows authentication is not supported. This is not a limitation in the data provider, it is simply not available in the Azure platform.

• Stored procedures are not supported.

• Federate authentication from the Windows Azure AppFabric is not supported.

Most of the common features are available, such as referential integrity, transactions, SQL authentication, and basically everything that is not expressly unsupported.

The Storage for the Application: The Intrinsic Database

So far, you learned that LightSwitch uses a SQL Server database at design time for development and debugging purposes. This database is called ApplicationDatabase.mdf and resides in the inData subfolder within the project. When you define new entities from scratch in your application, LightSwitch generates this database, which is also called the intrinsic database and is represented by the Application Data source in Solution Explorer. The intrinsic database on the development machine is attached to the local instance of SQL Server Express Edition, which is installed by the Visual Studio LightSwitch development environment.

When debugging the application, you can work with the database normally. So, you can insert, update, delete, and query data stored in the intrinsic database. When you deploy your application to production, LightSwitch can use SQL Server, SQL Express, or SQL Azure to host the intrinsic database. Remember that when deploying the application, LightSwitch does not deploy the whole database; it just deploys the schema. It also attempts to update a database schema if it already exists. With regard to this, LightSwitch reads the schema of the existing database and attempts to update it only with changes, such as adding new tables or relationships or adding new columns. Keep in mind that some changes are not updated to the schema because they would cause data loss. You can make all the changes you want during development, but if you have already deployed the database to production once, LightSwitch is protecting you from data loss.

Table 12.2 summarizes changes that are updated or not to the database schema upon deployment of the intrinsic database.

Table 12.2. Changes to the Intrinsic Database When Deploying the Schema to Production

Image

Of course, you might need to make changes to the intrinsic database schema during development, but this could also cause data loss. If you do not mind losing your data, you can make your changes. (A warning in the Data Designer displays.) The problem is if you have already deployed the database schema once, because at this point the update script used by LightSwitch will fail and roll back if potential data loss is detected. So, you should finish developing and designing your application and data before deploying the database schema. If you need to deploy data from the intrinsic database on the development machine, consider different tools, such as the Import and Export Data tool that ships with SQL Server. In Chapter 10, “Deploying LightSwitch Applications,” you learned how to configure connections to the intrinsic database on production machines and how to deploy the database schema.

Understanding the Membership Database

In Chapter 9, “Implementing Authentication and Authorization,” you saw how to use both Windows and Forms authentication to restrict access to the application resources to only the specified users and roles. In addition, LightSwitch has the concept of permissions, which define sets of actions allowed to the role that the current user belongs to. Users, roles, and permissions are also stored in the intrinsic database in specific tables. With particular regard to users and roles, LightSwitch uses internally the ASP.NET SQL Membership Provider, which requires such tables to be defined in the database. The list of such tables is shown here for your convenience:

dbo.aspnet_Applications

dbo.aspnet_Membership

dbo.aspnet_Profile

dbo.aspnet_Roles

dbo.aspnet_SchemaVersioning

dbo.aspnet_Users

dbo.aspnet_UsersInRoles

dbo.aspnet_RolePermissions

After the application has been deployed, you can add permissions and associate users with roles, but this is discussed in Chapter 9, with no further details about it at this point.

Understanding Data Providers: SharePoint 2010

LightSwitch also offers direct access to Microsoft SharePoint 2010. This is a fully featured data storage, with tables (lists) and relationships, but actually it is not a relational database, so the .NET Framework needs a different way to interact with it. What the .NET Framework really needs at this point is a service layer that can communicate with SharePoint 2010 and then expose data to the .NET environment. To accomplish this, Visual Studio LightSwitch uses the WCF Data Services technology, implementing a service that exchanges information between SharePoint 2010 and the LightSwitch application.

The OData service for SharePoint supports lists and relationships, whereas it does not support attachments and transactions. Fortunately, the OData service provides an object of type DataServiceContext that can translate data coming from SharePoint 2010 into entities that LightSwitch can handle the usual way.


Why Only Sharepoint 2010

Visual Studio LightSwitch supports only SharePoint 2010 (and possibly higher) because this version supports the OData protocol and therefore it is the only SharePoint version that can exchange information with LightSwitch. SharePoint 2010 exposes an OData feed that LightSwitch uses internally via a special handler.


Understanding which providers LightSwitch uses (and why) to access data from different storages is important for at least one reason: data types mapping. This is discussed in the next subsection.

Data Types Mapping: .NET, SQL Server, SQL Azure, and SharePoint 2010

Data can be of different kinds. You often work with (but are not limited to) strings, dates, numbers, and phone numbers. Each of these is represented by a data type. The .NET Framework 4.0 offers many built-in data types that cover lots of scenarios, and Visual Studio LightSwitch takes advantage of .NET data types to provide its own types system, which is more business oriented.

The types system in LightSwitch is specific to business solutions and not just a general-purpose set of types, as it is in the .NET Framework. When you work with the Entity Designer and the Screen Designer, you use data types as they are exposed by LightSwitch, including business types that have been mentioned several times already in this book. By the way, LightSwitch types are nothing but logical and business-oriented representations of .NET types. Therefore, when you write code, such as custom data validation or custom queries, you cannot use the LightSwitch types system because the code you write is purely .NET code; this implies that you must use types as per the .NET Framework. To better understand this, think of the Money type in LightSwitch. This logical type was introduced to represent amounts of money; however, when you validate entities in code, you cannot use such a type because it does not exist in the .NET Framework, so you need to use Decimal. For this reason, it is important to compare LightSwitch types to .NET types. This comparison is summarized in Table 12.3. Notice that for the sake of consistency, the .NET types are listed according to their name in the Base Class Library, and not with corresponding keywords of programming languages, so that both Visual Basic and C# use the same terminology.

Table 12.3. Comparison Between LightSwitch Types and .NET Types

Image
Image

Notice that LightSwitch supports types in external data sources that have a corresponding, appropriate type in the .NET Framework, such as the time type in SQL Server that is mapped to a System.TimeSpan. All the other types that do not have a counterpart in LightSwitch are ignored when importing data from an external data source.

Although it might not be a problem when importing directly from most SQL Server (or Azure) databases, it can be a problem when attaching a WCF RIA Service. For instance, if your WCF RIA Service exposes an Entity Data Model containing an entity that has a property of type System.Uri, this will be ignored and will not be imported in the LightSwitch Entity Designer because the type is not supported. So, pay attention to this when connecting to external data sources.

Another important thing to understand is the concept of nullable types. In the .NET Framework, nullable types can accept a null value, which means Nothing in Visual Basic and null in Visual C#. Nullable types are specific to value types, such as numbers, Boolean, and dates. This is important because nullable .NET types reflect types in the database that accept null values. So, when you design entities and specify the type for each property, the type of that property is not nullable unless you uncheck its Required check box.

For example, the autogenerated Id property of type Integer is not nullable because it is required and therefore cannot accept null values. When a type is nullable, you represent it in code by adding a question mark at the end of its name. For example, the InvoiceDueDate property in the Invoice entity accepts null values, so in code you represent it as a System.DateTime?.


Nullable Types Mapping in the Database

If an entity property is marked as not required, LightSwitch marks the corresponding SQL column as NULL. It marks the column as NOT NULL if an entity property is required.


An exception to this is the String type. In the .NET Framework, String is a reference type and therefore can always accept null values. So, the problem is when you mark a property of type String as required in the Entity Designer. Behind the scenes, the runtime treats a string as not nullable (required) by checking that it is neither null nor empty. So, basically, LightSwitch types are then translated into their .NET counterpart. Then the .NET Framework is responsible for translating .NET types into the appropriate types for the data source. For example, if you are working with a SQL Server database (like the intrinsic database for the application), the .NET Framework translates its types into SQL types that can be accepted by the data storage. But the contrary is also true, because the IDE can only import data types from data stores that have a LightSwitch counterpart. So, another important comparison that you need to make is between LightSwitch types and types specific to physical data storages, such as SQL Server, SQL Azure, and SharePoint 2010. Table 12.4 provides this comparison.

Table 12.4. Types Comparison Between LightSwitch, SQL Storages, and SharePoint

Image

With particular regard to SharePoint 2010, notice how only a few data types are supported and how the String type in LightSwitch maps several SharePoint types, such as single-line or multiline text, hyperlinks, and references to pictures. (There is no built-in image editor in SharePoint.) In addition, LightSwitch creates choice lists for choice menus in SharePoint.

Now that you have learned about the data storage, data providers, and data types, you have good knowledge of the data access tier. The next step is understanding the layer that performs data operations: the logic tier.

Understanding the Logic Tier

The logic tier is positioned between the data storage tier and the presentation tier and is responsible for data access and data processing. The logic tier is made of several components:

• Data services, which are responsible for accessing to data sources

• Queries, which are data-related operations for retrieving, sorting, and filtering data

• Submit operations, responsible for sending information to the underlying data source

• Transaction management, a service responsible for database transactions

• Data providers, a number of .NET data access frameworks for working with data in a managed fashion

The following subsections provide a detailed explanation of each component of the logic tier.

Data Services

When you create or attach a data source, Visual Studio LightSwitch generates a data service. So, there is a data service for each data source. Basically, a data service provides access to a data source and exposes both entity sets and data operations (such as queries and the submission of changes to the database). Changes include all the added, updated, and deleted entities within entity sets. A group of changes is called a change set.

From a .NET point of view, a data service is represented by a class that inherits from DataService. For example, you have an ApplicationDataService class that interacts with the intrinsic database and a NorthwindDataService class that interacts with the Northwind database as an attached data source. These classes expose members for performing data operations against the data source, such as querying, inserting, updating, deleting, and saving changes. Data service classes are defined inside an autogenerated code file that is located under the Server project, GeneratedArtifacts folder, but this is explained in the last part of this chapter. Behind the scenes, data services are defined upon entity data models (EDMs) based on the ADO.NET Entity Framework. This allows implementing entity types, entity sets, associations, association sets, and foreign keys mapping in a managed object model. In addition, EDMs provide the appropriate infrastructure to query and edit data, and to save data to the data store. However, in LightSwitch, you cannot take advantage of all objects within an EDM, such as stored procedures. Version 1.0 of LightSwitch does not support this scenario. Figure 12.2 shows how the data service for the application database is defined.

Image

Figure 12.2. Example of data service definition.

As you can see, the data service exposes entity sets that can accept data operations such as queries and submit. Both kinds of data operations are now discussed further. The biggest benefit at this point is that the developer does not need to know how LightSwitch interacts with different data sources (SQL, SharePoint, and so on), because the EDM provides a unified data object model independent of the underlying source.

Queries

As you know, a query fetches a set of entities from an entity set exposed by the data service. Queries can optionally accept parameters, filtering, and sorting and can return multiple entities or a single instance of an entity. Queries have already been discussed in detail in this book, so here you get a recap that lets you understand how and why they are placed within the logic tier.

When you create or attach a data source, LightSwitch automatically generates some default queries for each entity set. As you learned in Chapter 11, “Handling Events in Code,” LightSwitch generates All and Single queries. For example, for the Orders entity set, LightSwitch generates a method called Orders_All, which returns the full list of entities from the entity set, and Orders_Single, which returns the instance of an entity set by key. (Actually, there is also SingleOrDefault, which returns null if the specified key returns no result.) As you learned in Chapter 5, “Customizing Data Validation,” you can create your own queries based on existing ones as a kind of inheritance, such as for the OrdersFromTheUnitedStates query.

You can create queries by using the Query Designer but also customize queries in code; for example, writing LINQ queries. If you use LINQ, note that not all the standard query operators are supported, just those that produce results that can be serialized by WCF RIA Services.

Chapter 5 also provided an example of queries accepting parameters. This is useful when you want to let the user select an entity and then show related entities for the current selection, such as retrieving orders for the specified customer by key. Note that clients can compose additional queries, which are associated with screens instead of being associated with the data service. In this case, such queries are always executed in the data service, and not on the client. The query execution then passes through the query process, which is made up of the following methods: CanExecute, Executing, PreProcessQuery, and Executed (or ExecuteFailed if the query execution fails). Behind the scenes, the runtime transforms queries into LINQ expressions that are processed by the .NET Framework.


Queries and Place of Execution

When you design or write queries within the data service, such queries are executed on the server. LightSwitch transforms the query into a LINQ expression tree and sends this to the server for processing. The result of the query is then sent back to the calling code. This involves querying objects of type EntitySet or IDataServiceQueryable.

According to the MSDN documentation, queries executed on the server perform well because only entities that match the query are returned. However, you cannot use all the standard LINQ query operators, just those supported by the Entity Framework and WCF Data Services. When you compose queries at the screen level, the runtime works against the in-memory collection.

As you can imagine, this can affect performances if the collection of entities is large, but you can use the full set of LINQ operators. The reason is that queries at the screen level can return EntityCollection and IEnumerable; both types support all the standard LINQ query operators.


Support for Static Spans

Visual Studio LightSwitch has support for so-called static spans, which is the ability of including or excluding related data when executing a query, as in the case of a Customer entity having a one-to-many relationship with an OrderHeader entity. With static spans, LightSwitch can return all data for a row with a single round trip. This is done by launching a single query across multiple entities. Then the runtime analyzes the query and includes or excludes data as needed. This behavior is optimized for better performances.

By default, static span returns include all related data in the query, but you can change this behavior via the Manage Included Data hyperlink on the Query Designer that you access from screens. To see how this works, follow these steps:

1. With the Invoice Management application open in Visual Studio LightSwitch, in Solution Explorer, double-click the Search Orders From The USA screen. This screen is selected just for demo purposes.

2. In the upper-left corner of the Screen Designer, click the SearchOrdersFromTheUSA query, and then click Edit Query.

3. When the Query Designer appears, in the Properties window, locate and click the Manage Included Data hyperlink. Doing so launches the same-named dialog, shown in Figure 12.3.

Image

Figure 12.3. Managing static spans with the Manage Included Data dialog.

As you can see, you can decide the behavior for loading related data (Included or Excluded), and this is also available for nested relationships. If you do not have particular requirements, the default behavior provides an already optimized query-execution mechanism. This is basically why this topic is discussed in a chapter related to architecture rather than to queries.

The Validation Framework

You saw in the first chapters of this book how Visual Studio LightSwitch makes it easy to run validation rules. As you know, a validation rule is a piece of code that is responsible for ensuring that the user input is valid, according to your business requirements and to the data source structure. All the data types in LightSwitch have built-in validation rules (declarative rules), but you can write your custom ones when required (imperative rules). These are well-known concepts, so they are not covered again this chapter. Instead, here you learn interesting concepts about architecture and about how the validation engine works behind the scenes, which is in the logic tier. To understand how such an engine works, you have to remember what piece of data goes under the execution of validation rules; basically, validation rules apply to entities as a whole, to entity properties, and to screen properties. This distinction is important to understand the place and the moment at which validation rules run. Table 12.5 summarizes how validation rules run.

Table 12.5. Execution of Validation Rules

Image

The runtime enforces the validation mechanism, and that client checks that data is valid before sending it to the middle tier, and this checks data again before sending it to the data store. This is particularly important in multiuser environments, as discussed shortly. Notice that all validation rules, either built in or custom, are treated the same way and follow the behavior summarized in Table 12.5.

Understanding Validation Results

When a validation rule fails, it generates a result. As you remember from Chapter 5, this can be an error, a warning message, or an informative message. Each validation result is added to a collection of type EntityValidationResultBuilder. Once a validation result is added to this collection, it is notified to the end user via the screen UI. This was shown when you wrote custom validation rules, but it may be also interesting to know that such a collection can be accessed in code. The Details property of screens allows accessing this collection like this:

Me.Details.Properties.Customers.ValidationResults

This example refers to a Search Screen, so the Details.Properties property points to the screen collection (Customers). If the screen shows a single entity, however, you can access validation results directly on the entity property as in this code, which assumes that the screen is displaying a single Customer entity:

Me.Details.Properties.CompanyName.ValidationResults

There is something to say about saving changes. If the user attempts to save changes for a screen that contains validation errors, no matter if the save operation is invoked via the Save button or in code, all built-in and custom validation rules run on the client, over modified and newly added entities. If any rule fails (except for warning and informational results, which are ignored), the save operation is canceled so that the user is notified of the errors and can fix them before trying to save the changes again. If no errors are encountered, the runtime sends the changes to the server. Here validation rules established at the entity and at the DataService level run. This is an enforced mechanism that ensures that during the time in which the runtime sends changes from the client to the server, no bad edits have been done over data. This is because multiple clients could be working on the same piece of data, so this check is really important. If the validation results on the server are successful, changes are sent to the data store. If the validation results on the server are not successful, the save operation is rejected, and validation errors are returned to the client and notified to the user, who will have to fix errors before attempting to save again.

Dependency Calculation

The validation engine has a complex architecture, but it is also very powerful. Another important thing to know is that it caches validation results to avoid the need of rerunning rules every time these are required for a particular piece of code or data. The real problem is that when data changes, cached validation results become invalid. The runtime solves this problem by introducing the concept of dependency tracking.

Simply put, the runtime keeps track of and stores all properties involved in a validation rule when this is executed. For example, if a validation rule accesses the PostalCode property of the Customer entity, Customer.PostalCode gets registered as a dependency to the validation rule that runs over that property. If that property changes, the runtime schedules the validation rule to rerun, but if that property does not change, the first validation result is cached, and the validation rule does not again until the property changes. This is a huge benefit: As a developer, you are no longer responsible for writing code to check property changes and for running validation rules according to those changes, because this is automatically done by the runtime for you.

Understanding INotifyDataErrorInfo

As you know, when a validation error occurs the user is notified of validation failures through the validation box within a screen. Validation results are sent to the client via a collection of type ValidationResults, as mentioned before. LightSwitch applications rely on Silverlight 4, but the problem is that Silverlight has no built-in support for ValidationResults, because validation failures in Silverlight are represented via the INotifyDataErrorInfo interface. All objects exposing the ValidationResults property also implement the INotifyDataErrorInfo interface so that they can be simply bound to the screen user interface, and this is why you do not need to write code to display validation results. In addition, this enables developers to create custom controls to replace the default validation viewer and makes it easy to bind validation results to controls that can retrieve information from INotifyDataErrorInfo.


Difference Between INotifyDataErrorInfo and ValidationResults

Even if INotifyDataErrorInfo could simply look like a wrapper to expose objects from ValidationResults in a way that could be understood by Silverlight controls, behind the scenes the difference is that ValidationResults returns validation results, if any, regardless of the entity state. In contrast, INotifyDataErrorInfo filters validation results, excluding those for unmodified entities. This reduces overhead and improves performance, especially when you have thousands of entities.


Submit Operations: The Save Pipeline

Data service classes expose one method called SaveChanges for submitting changed entities to the data store. When the client invokes this method, changed entities are sent to the data service for processing and then sent to the storage. The method is automatically invoked by the client when clicking the Save button on the application’s main screen, but as you know, you can invoke it manually like this:

Me.DataWorkspace.ApplicationData.SaveChanges()

At this point, it is important to emphasize that a client has its data workspace in which it keeps track of changes over entities per data service. This set of changes over entities is also known as a change set. A change set includes all the added, updated, and deleted entities for the data service that the client is working on and is represented by an instance of the EntityChangeSet class. When the user clicks Save on the screen, the client serializes the change set, which is sent to the data service and then on to a workspace on the server where it is deserialized and processed through the so-called save pipeline. This is made of different phases of execution, as follows:

1. Preprocessing: This involves invocations to SaveChanges_CanExecute to check whether the user has permissions and to SaveChanges_Executing, which is called before the operation starts.

2. Processing entities: This involves invocation to Validate, Inserting, Updating, Deleting methods for each entity sets. As you can see, method names are self-explanatory. In addition, in this phase, LightSwitch checks that operations are allowed for the current user by invoking CanInsert, CanUpdate, CanDelete, and CanRead.

3. Execution: Changes are sent to the underlying data provider for processing.

4. Post-processing over entities: This involves invocations to methods running after changes have been saved, such as Inserted, Updated, and Deleted for each entity set.

5. Post-processing: This involves invocations to methods such as SaveChanges_Executed (the save operation was successful) or SaveChanges_ExecuteFailed (the save operation failed).

To ensure that all changes are saved correctly (including validation), changes within a change set are processed at least once. With particular regard to validation, if this fails over an entity the save pipeline stops and throws an error to the client and rolls back the database transaction. If you instead add business logic in the Inserting, Updating, or Deleting entity methods, the LightSwitch runtime ensures that entities affected by these methods are also processed in the save pipeline. When the save pipeline completes, changes are serialized back to the client so that it receives updated entity IDs or other database default values. (This also allows reflecting changes to the user interface.) Finally, the save pipeline updates data by using optimistic concurrency. In case of a concurrency violation, the runtime sends an error to the client, which can then decide which values to keep among current, proposed, and server values. There are several other interesting aspects to consider in the save pipeline, two of which are discussed in the next subsections.

Working with Change Sets

It can sometimes be useful to inspect which entities have been changed within a change set. Both client and server code can accomplish this by invoking the GetChanges method exposed by the Details property of the data source. This method returns an instance of EntityChangeSet, which contains a collection of changed entities in the change set. This type exposes three properties that you can investigate further to understand which entities were added, modified, or deleted via three corresponding properties: AddedEntities, ModifiedEntities, and DeletedEntities. For example, the following code demonstrates how to retrieve the list of deleted entities within the change set:

'Creates a new StringBuilder for efficient
'string concatenation
Dim currentChanges As New Text.StringBuilder

'Retrieves all changed entities in the current
'change set
Dim changeSet As EntityChangeSet = _
                 Me.DataWorkspace.
                 ApplicationData.Details.GetChanges

'Iterates through deleted entities
For Each entity As IEntityObject In changeSet.DeletedEntities
    currentChanges.AppendLine("Entity ")
    currentChanges.Append(entity.ToString)
    currentChanges.Append(" has changes")
Next

The code snippet stores the list of deleted entities into a string, but of course, this is just an example. You could handle information from change sets to inform the user about entities involved in the save pipeline or to write actions performed against entities into a log in the database.

Handling Exceptions in the Save Pipeline

When the save process fails, the runtime aborts the current transaction (if any), invokes SaveChanges_ExecuteFailed, and throws one of the most appropriate exceptions from the .NET Framework, such as ValidationException (entity validation failed), PermissionException (no permissions to modify an entity), or ConcurrencyException (a concurrency violation occurred). However, the runtime does not return the aforementioned exceptions. Instead, it returns an instance of DataServiceOperationException, in which the InnerException property represents the original exception. Actually, InnerException is not serialized back to the client, but the Message property of DataServiceOperationException is useful enough because it contains the error message from the original exception.

You can also add your exception-handling logic by using Try..Catch blocks. When exceptions are handled, the save pipeline continues without returning any exceptions. Notice that you do not handle exceptions within SaveChanges_ExecuteFailed, because this is invoked after exceptions have already determined, so you generally place your error-handling logic within methods invoking SaveChanges. The following code demonstrates how to intercept an exception and determine its type of origin:

Try
    Me.DataWorkspace.ApplicationData.SaveChanges()
Catch ex As DataServiceOperationException
    If TypeOf (ex.InnerException) Is ConcurrencyException Then
        'Apply your logic to handle a ConcurrencyException
    ElseIf TypeOf (ex.InnerException) Is ValidationException Then
        'Apply your logic to handle a ValidationException
    ElseIf TypeOf (ex.InnerException) Is PermissionException Then
        'Apply your logic to handle a PermissionException
    End If
End Try


The Save Pipeline Unleashed

This subsection discussed in detail the save pipeline, providing lots of useful information to interact with the process. However, you want to consider some other things, too, such as concurrency failures, security, executing queries within the pipeline, and diagnostics. Although useful to know, these techniques are not usually used in code by the developer because the runtime in LightSwitch does the appropriate job for you. But if you want to study these techniques in detail, you can read an article by Dan Seedfelt (senior program manager on the Visual Studio LightSwitch team), who discusses interesting architectural aspects of the save pipeline. The article is titled “Getting the Most out of the Save Pipeline in Visual Studio LightSwitch,” and you can find it at http://bit.ly/elbtDD.


Transaction Management

From a pure database-oriented perspective, transactions are units of work responsible for recovering the database to a consistent state in case of failures or if multiple operations remain uncompleted. In addition, transactions are responsible for providing isolation between concurrent accesses to the database from different applications. The LightSwitch runtime automatically manages transactions on behalf of the developer, so situations in which you will interact in code with transactions are very rare, but it is useful to know that you can do that. In this case, you write code as per the System.Transactions library in the .NET Framework.


System.Transactions

The official documentation about the System.Transactions library is available in the MSDN library at http://msdn.microsoft.com/en-us/library/system.transactions.aspx.


In LightSwitch, transactions have a scope, which is the data workspace. Therefore, every single operation gets the instance of the data workspace it belongs to, and each data workspace has its own transaction and connection to the database. It is possible to nest transactions if the save pipeline is initiated from within another save pipeline. In theory, LightSwitch can also use ambient transactions (see System.Transactions), but there is no ambient transaction by default, because LightSwitch has been designed to not support the Distributed Transaction Control. In addition, by default, LightSwitch saves changes to a single data service, which has the data workspace in which the transaction is scoped. However, it is possible to send changes to another data source by making changes to entities in the other data source in the current workspace and then invoking the SaveChanges method from the other data source. If you ever decide to control the default transaction scope yourself, you need to follow these steps:

1. Create the transaction scope in the SaveChanges_Executing method.

2. Commit and close the transaction scope in SaveChanges_Execute.

3. Configure the appropriate data sources the Distribute Transaction Controller; this is required if you want to transact changes between multiple data sources.

Of course, transactions are supported in most databases, but not in other sources such as SharePoint 2010. In this case, transactions are ignored. As a consequence, the save pipeline will abort at the moment at which an exception occurs, but any previous changes made in the pipeline will remain committed. This is different from what happens with databases, in which change sets are saved as an all-or-nothing operation. After the discussion about queries, the save pipeline, and transactions, you know how data operations work, but you still do not know what technology makes them work that way. This is explained in the next subsection.

Data Providers

It is not new that LightSwitch enables developers, using the same model, to create applications that interact with different kinds of data sources. This is possible because of the concept of data providers. A similar concept was presented in the previous section about the data storage tier. In the logic tier, a data provider is a component based on the ADO.NET Entity Framework 4 required to talk to specific data sources, and implements an application programming interface (API) for querying and editing data. This kind of API is private, meaning that it is used internally from the logic tier to the data storage, but it cannot be exposed publicly to other developers.

There are three types of data providers in the logic tier:

• ADO.NET Entity Framework to access SQL Server, SQL Azure, and other databases having an Entity Framework-based provider.

• WCF RIA Services over the OData protocol to access SharePoint 2010. This is an internal implementation, and it is not a traditional service, but it is good for you to know this.

• WCF RIA Services for custom data sources.

With regard to the first provider, LightSwitch does not need to perform marshaling between entity types in the application and entity types in the Entity Framework because both rely on EDMs. With regard to the second provider, LightSwitch infers an entity model based on the result of the mapping between SharePoint types and LightSwitch types, and then it marshals between internal entity types and types supported by the underlying data store when sending changes back. Finally, with regard to the third provider, LightSwitch infers an entity model based on types exposed by the service and also infers an entity set based on a default query in the service.

This kind of architecture enables you to use the same approach, the same types, and the same tools within LightSwitch regardless of the underlying data source.

Understanding the Presentation Tier

The last tier in the LightSwitch architecture is the presentation tier, or simply the client. The client allows user interaction, such as data entry and data visualization, and it is nothing but a Silverlight 4.0 application that can run either as a desktop client application for Windows or as a web application. As you already know, Silverlight 4 applications can run as desktop clients by enabling the out-of-browser functionality. The presentation tier plays a fundamental role in the architecture not only because it allows user interaction but also because it must provide a full and intuitive user experience. This involves the following areas:

• Offering an intuitive and nice appeal for the user interface

• Offering a responsive user interface via asynchrony and multithreading

• Keeping track of changes to entities and handling data validation

• Handling efficient communications with the logic tier and the data tier

• Providing an easy and intuitive mechanism to receive application updates

As you can imagine, behind the window that you see onscreen and that LightSwitch generates for you, lots of hard work went into putting together those powerful .NET technologies in the most efficient way possible. From an architectural point of view, the presentation tier is divided into three main areas: the shell, screens, and the data workspace. Each area is then divided into subareas that are discussed in this section. Figure 12.4 shows this architecture.

Image

Figure 12.4. The architecture of the presentation tier.

As you can see in Figure 12.4, each main area in the architecture is made up of some services that are discussed here.

The Shell

You can think of the shell in LightSwitch applications as of the root-level user interface elements, such as the main window with the taskbar and root-level buttons. The shell also involves the hosting service for running out-of-browser clients. Services comprising the shell are described now in more detail.

The Hosting Service

In LightSwitch, and more generally in Silverlight, applications are produced as .xap packages, and so they cannot be launched directly. Therefore, they need a hosting service that offers platform support to the execution. The ways an application is hosted depends on how it is deployed, which can be either as a desktop client or a web client. About desktop clients, the runtime launches a hosting process called SLLauncher.exe, which is responsible for running .xap packages out of the browser. In addition, this process assigns elevated permissions to the LightSwitch application, so that it can leverage features such as Office interaction via COM or the built-in Export to Excel functionality. In contrast, web clients are hosted by the web browser, so there is no need of an additional process (in contrast to desktop clients). Web clients do not have elevated permissions, and therefore they can neither interact with the hosting system nor offer, as a consequence, the Export to Excel functionality. The hosting service in the LightSwitch shell is very important because it is the first layer in the architecture and provides support for running the application.

The Shell UI

The shell is the main element in the user interface and provides root-level elements such as the main window and the root Ribbon Bar and includes built-in functionality such as logging in to the application and launching screens. Figure 12.5 shows the default application shell.

Image

Figure 12.5. The application shell.

LightSwitch offers a default application shell, so you do not need to create one on your own every time you build an application. By the way, the shell UI is a pluggable component, meaning that you can replace the default shell by downloading or creating a custom one, and so you can provide a different look and feel to your applications, but also place root elements in the UI in a different manner according to the target of your application. For instance, applications running on multitouch screens might need a different arrangement for the shell UI, and this can be accomplished by replacing the default one with a more convenient definition.

You create custom shells using an extensibility package, which is discussed in Chapter 18, “LightSwitch Extensibility: Themes, Shells, Controls, and Screens.” The shell is also the place in which the Model-View-ViewModel (MVVM) pattern comes into play. In a few words, MVVM is an architectural pattern used in Windows Presentation Foundation (WPF) and Silverlight applications. It provides deep abstraction between layers in the application so that data (the model) and the user interface (views) can interact without knowing each other, as a result of an intermediate layer (view model) that exposes commands based on the ICommand interface and that can be bound to controls that have a Command property. Of course, view models can expose other kinds of actions, too, but commands are the most important because they rely on two familiar methods in LightSwitch: Execute and CanExecute. As it is easy to understand, Execute represents the action that runs when the command is invoked, and CanExecute represents the condition that makes a command runnable.


Readings About MVVM

The author of this book has also published a blog post series about getting started with MVVM in WPF applications. The full series summary is available at http://bit.ly/ajgFgd.


Components in the shell UI based on the MVVM are the following:

• Screen activation and navigation between screens

• Active screens

• Commands in screens

• The data-validation mechanism at the screen level

• The application logo

• Information on the current user

Summarizing MVVM is not easy, but you actually do not need to know how it works. You just use built-in UI components in LightSwitch and the IDE automatically binds them to the appropriate view models.

The Theming Service

The presentation tier manages the appearance of colors and fonts within controls via a basic theming service. LightSwitch offers a default theme that is automatically assigned to the user interface when you create an application, but you can also create a custom theme. This is possible because themes in LightSwitch rely on Silverlight’s styles, so you just need to define a set of styles for fonts and colors for each control that you want to restyle and then select the new theme. You learn how to create custom themes in Chapter 18.

Screens

Screens are elements in the client user interface that enable interaction between users and the application. Screens run isolated from other screens, which means that screens do not share data and that multiple screens can be shown concurrently (including screens of the same type) but the shell can focus on the currently active screen. Screens are made of three layers: data, layout, and visual tree. Because screens follow the MVVM pattern, each layer in a screen also represents a layer in the pattern. The three layers are discussed now in more detail.

Screen Data

When you create a new screen, LightSwitch generates a business object that contains data and business logic for that screen, wrapped from the logic tier. The new business object resides on the client, but it has no user interface. This layer represents the model in the MVVM pattern. Basically, when you write code against the screen, you actually write code against the screen data. This layer exposes entity properties, query results in the form of screen collections, and commands. Then LightSwitch can keep track of details for each data item, including the state and validation results. Screen data is not shared across screens, so each screen has its own copy of an entity or entity set. The data workspace, which is discussed later, is responsible for keeping screen data isolated among screens.

Screen Layout

The screen layout is a container of view models representing specific data elements on the screen. For a better understanding, it is a good idea to consider a practical scenario. Consider the EditableProductGrid screen in the Invoice Management application. As you can see on the Screen Designer, an element named Products is represented by a Data Grid control, and there is a Command Bar containing buttons such as Add, Edit, and Delete. Products is a collection of entities, whereas buttons execute a number of commands. Here a view model exposes the Products collection picking up data from the screen data layer and a number of functions, one for each command, under a form that can be data-bound to Silverlight user controls. So, the view model is not an element in the user interface; it is instead a kind of bridge that exposes data and commands in a way that is acceptable to user controls. Because you can have multiple items, the screen layout control can be considered a container of view models. Figure 12.6 shows the items exposed by the view model for the Products element.

Image

Figure 12.6. Locating screen data.

You know that you can drag data items onto the Screen Designer. This action actually binds data and commands from the view model to user controls on the screen. The latter represent the content tree.

Screen Content Tree

The content tree represents the view in the implementation of the MVVM pattern and can be compared to the visual tree concept of WPF and Silverlight. Basically, the content tree is the tree of controls and data bindings that present the screen. Each control or data binding is also referred to as content item. Figure 12.7 shows what the screen content tree looks like in the Screen Designer.

Image

Figure 12.7. The screen visual tree.

As an example, consider the content tree for the Editable Products Grid screen. It consists of the following:

• The Rows Layout container, which nests a Screen Command Bar control; this contains two Button controls

• The Data Grid control, which nests a Command Bar control; this contains three buttons

• The Data Grid Row control, which contains a number of user controls such as Text Box and Auto Complete Box to present data

When you create a new screen, LightSwitch walks through the screen layout just discussed and automatically adds the appropriate controls and buttons to the screen, based on the data properties and methods exposed by the view model. You can then replace the default selection with different controls (for instance, by replacing the Data Grid with a List), including custom Silverlight controls, as you will see later in Chapters 16 and 18.

If you are not new to WPF and Silverlight and have an idea about how data binding works, you might be interested in knowing that a control is populated with the underlying view model by assigning its DataContext property at runtime. As a consequence, nested controls are populated via data binding. The content tree is totally generated at runtime. What you see in the Screen Designer is actually a graphical representation of what LightSwitch is going to generate and use, but the whole user interface is generated when the application is starting. This is why you do not find any XAML code in LightSwitch applications. In addition, this prevents inexperienced users from accidentally modifying elements in the UI and provides the required support for the Immediate Customization feature, which allows rearranging elements in the user interface at runtime.

This approach also makes it possible for LightSwitch to automatically remove or rename items from the screen when a property is removed or renamed from an entity. In fact, the LightSwitch infrastructure can monitor changes to your application at design time and make appropriate updates to screens, queries, and code so that you do not end up with any errors when your data model changes. This also isolates the LightSwitch developer from the underlying UI technology, because you simply describe the model of your application, and then LightSwitch is responsible for presenting it; this is how a model-based architecture works.

The Data Workspace

The last layer in the presentation tier’s architecture is the data workspace, which is responsible for managing data on the screen. Basically, the data workspace is on both the presentation and middle tier; the change set is marshaled to the middle tier when the user saves data. From a screen perspective, it allows screens to keep their own local copies of the entities, so that data is not shared with other screens. To handle data, the data workspace relies on the components discussed in the following subsections.


Entities, Relationships, and Queries

In Figure 12.2, you saw how the data workspace in the client manages entities, relationships, and queries. These concepts are not discussed again here because you already know about them in detail (even though they were discussed with regard to the data layer). The concept remains unchanged. The difference is that in the presentation tier, you have a local copy of the data and you work with it locally before sending it back to the server.


The Data Service Client

When the logic tier was discussed, you learned that a data service for accessing data is created every time you create a new data source or attach an existing data source. The client accesses data services via the data service client. Basically, a data workspace on the client provides one instance of the data service client for each data service in the logic tier. This allows the client to fetch data and also to perform data validation at the screen level. Validation occurs before sending changes to the logic tier. The data service client also handles entities and tracks changes for the local copy of entities that each screen owns (client-side change set), related to the corresponding data service.

About saving changes from the client, every screen has a Save command that invokes the data service’s SaveChanges command. This submits the change set that the active screen is associated with. This is useful to know if you want to override the default code associated with the Save button on screens. The following code snippet shows an example in which the appropriate SaveChanges method is invoked within a Try..Catch block that shows a message box in case of failure:

Private Sub EditableProductsGrid_Saving( _
            ByRef handled As Boolean)
    ' Write your code here.
    Try
        Me.DataWorkspace.ApplicationData.
            SaveChanges()
    Catch ex As Exception
        ShowMessageBox(ex.Message)
    Finally
        handled = True
    End Try
End Sub

Managing Entities

When creating screens, LightSwitch assigns entities and entity collections to screens. As you know, entities encapsulate both data and business logic, such as validation rules. Even though entities are not shared across screens, they are shared across the screen and the logic tier. This provides a huge benefit: By writing the business logic only once, LightSwitch executes such logic on both the presentation tier and the logic tier. At this point, the discussion about the architecture of LightSwitch applications can be considered complete. You know what happens behind the scenes, you know how applications are organized, and you know how LightSwitch applies architectural patterns. Actually, you are still missing one piece: understanding how the architecture layers are put together within a Visual Studio solution, which is the subject of the next section.

Dissecting LightSwitch Projects

Visual Studio LightSwitch relies on the shell of Visual Studio 2010, which is a sophisticated environment. If you have previous experience as a developer working with Visual Studio 2010, you know how many project templates, how many libraries, and how many files can make up a Visual Studio solution. LightSwitch has a different goal: making things easier. This approach also affects the IDE usage and how you interact with projects. For instance, the first door you open to interact with projects is Solution Explorer. Within this tool window, you are used to seeing all the elements that comprise projects and solutions, but in LightSwitch, the perspective is different. In fact, the structure of Solution Explorer in Visual Studio LightSwitch simply reflects the approach of making things easier, so you just see two kinds of items: data and screens (logical view). You can see additional projects in the solution if you are working with Visual Studio 2010 Professional or higher and you add Silverlight user controls or class libraries, but the way in which LightSwitch projects are presented remains unchanged.

The real story is that behind the scenes, LightSwitch projects are 100% Visual Studio projects based on templates that are specific to the new environment. Therefore, you have a solution file (.sln) containing several Silverlight projects (.vbproj files if you choose Visual Basic or .csproj files if you choose Visual C#). The reason why you do not see how projects are actually organized is that you do not need to make changes manually, because you focus only on the business logic. In any case, you want to understand how LightSwitch projects work for a number of reasons:

• Understanding the architecture of a technology enables you to leverage that technology better.

• You might need to add references to external libraries, especially control libraries.

• You might need to add code files (for example, static classes that implement extension methods).

So, at this point, you might want to know how to interact with LightSwitch projects the old-fashioned way. What you need to do is enable the File View in Solution Explorer by clicking its button on the toolbar of this window. Figure 12.8 shows how to locate this button.

Image

Figure 12.8. Enabling the File View.

When you want to revert to the logical representation of the project, just click Logical View. At this point, Solution Explorer presents the real structure of the solution, which consists of five projects. These are explained in the next subsections.


Enable the All Files View

With File View, you also need to click Show All Files on the Solution Explorer toolbar; otherwise, the ServerGenerated and ClientGenerated projects will remain hidden.


Overview of LightSwitch Solutions

As mentioned previously, a LightSwitch solution is a 100% Visual Studio solution. A LightSwitch solution is made of one root element, which is a Silverlight project, and five nested projects; four of them are Silverlight class libraries, and one is an ASP.NET web project that hosts the Silverlight package that represents the application. The root project contains some important files:

App.config: This is a configuration file at the application level and contains some settings used by the application and the connection string to the database generated during the development life cycle.

Silverlight.js: This is a JavaScript file that is embedded within web pages running the LightSwitch application.

default.htm: This is the default HTML page that hosts the application’s Silverlight package (.xap) as a user control and that is automatically opened when the application starts.

ApplicationDefinition.lsml: This XML-based file contains the full list of elements that are part of the application. This includes entities, queries, screens, functions and procedures, tasks groups definitions, and so on. It is used by the LightSwitch runtime to generate both the user interface and the support libraries.

All these files can be opened and examined via the Visual Studio editor by double-clicking their names in Solution Explorer. The solution also contains other five projects, as described momentarily.


Be Careful: Never Change That Code!

The next subsections describe autogenerated code that LightSwitch uses to maintain the application’s infrastructure at runtime. By the way, what you will get is a description of what a code file implements, but not instructions on how to change it or customize it. The reason is simple: Autogenerated code must remain exactly as you see it, because the smallest edit could prevent the application from working, although the IDE is intelligent enough to regenerate most of the required code. If you want to customize existing code or add custom code, follow the steps described in Chapter 11, “Handling Events in Code,” but do not directly modify code files that you see in the File View.


Server-Side Projects

Visual Studio LightSwitch generates two server-side projects: Server and ServerGenerated. These projects are considered as server-side because they implement data models and code in the middle tier that runs against data on the server. Figure 12.9 shows how both projects appear in Solution Explorer.

Image

Figure 12.9. Server-side projects as they appear in Solution Explorer.

Understanding ServerGenerated

The ServerGenerated project is basically an ASP.NET web application that acts like the host for the Silverlight package representing the application. As you also discover for other projects, you see a list of folders, but most of them are empty; the reason is that they will be populated at runtime. This is common in LightSwitch because a lot of parts of the application are generated at runtime, such as the user interface. This makes sense because in LightSwitch, you must focus on your business logic, not other parts of the application. Table 12.6 describes the important elements that you need to know in this project.

Table 12.6. ServerGenerated Project’s Content

Image

This project produces libraries that are responsible for exposing data to the middle tier through the network.

Understanding Server

The Server project implements the middle tier, which is the place for the business logic and data validation running on the server. This project produces a .dll class library that has references with the other projects in the solution. Table 12.7 summarizes the most important elements in the project.

Table 12.7. Server Project’s Content

Image

As in ServerGenerated, a number of empty folders are populated at runtime so that the developer has no interaction with generated elements.

Middle-Tier Projects

The next project that you have to consider is called Common.

This project produces a library (and therefore contains code) that runs in the middle tier and presentation tier and that is responsible for handling permissions, data validation over entities, and other important code that needs to run on both tiers. Figure 12.10 shows how the project appears in Solution Explorer.

Image

Figure 12.10. The project Common in Solution Explorer.

All the most important elements are summarized in Table 12.8.

Table 12.8. The Common Project’s Content

Image

The Common project is also important for another reason. In fact, if you need to inject custom code to the application that runs on both the client and the server, this is the appropriate place.

Client-Side Projects

So far, we have discussed projects that produce libraries running on the server side, including the middle tier. Two other projects are committed to producing the client part of the application, ClientGenerated and Client. Figure 12.11 shows how they appear in Solution Explorer.

Image

Figure 12.11. ClientGenerated and Client projects are visible in Solution Explorer.

Both are now discussed in more detail.

Understanding ClientGenerated

The ClientGenerated project is basically a handler of items generated at runtime, related to the data management from the client perspective. For instance, ClientGenerated is responsible for data validation processing on the client. What you can see in Solution Explorer is that this project stores the application’s icon, digital certificates required for desktop deployment (.pfx files), and a folder called GeneratedArtifacts that contain a code file named Application.vb (or .cs) that contains code for creating the instance of the application and for retrieving the current instance (although you will never use this code directly).

Understanding Client

The Client project is totally oriented to the user interface definition. Table 12.9 summarizes the most important files available in this project.

Table 12.9. Client Project’s Content

Image

It is important to provide some more information on Application and Screen files. The first code file basically implement methods that are exposed by the Application class and that are used by LightSwitch (or by the developer) to launch screens. All methods that you used back in Chapter 11 are implemented in this file. It also defines support classes for handling data correctly in the user interface. For example, the following code snippet picked up from the Application class demonstrates how the method that launches the Search Orders screen is implemented:

<Global.System.CodeDom.Compiler.
        GeneratedCodeAttribute("Microsoft.LightSwitch.BuildTasks.CodeGen",
        "10.0.0.0")> _
        <Global.System.Diagnostics.DebuggerNonUserCodeAttribute()> _
        Public Sub ShowSearchOrders()
            DirectCast(Me.Details,
                       Global.Microsoft.LightSwitch.Details.
                       Client.IClientApplicationDetails).
                       InvokeMethod(Me.Details.Methods.ShowSearchOrders)
        End Sub

The most important things to emphasize here are the DebuggerNonUserCodeAttribute, which tells to the compiler that this code is generated by the IDE, and the fact that the InvokeMethod method actually calls a property called ShowSearchOrders that represents an instance of the screen within the application. This behavior is common to all other screens. Notice that you will find another partial definition for the Application class under the UserCode folder. This additional partial definition contains code that checks whether the current user has permissions to run the screen or queries associated with the screen. This topic is part of the access control, which is discussed in Chapter 9. Screen, instead, defines classes that represent instances of screens that you created in the Screen Designer and that LightSwitch uses at runtime. Every class exposes methods that you see in the Write Code drop-down list in the Screen Designer. Remember that the code that you reach via Solution Explorer is used by elements of the user interface, but you cannot access the user interface definition.

The reason is obvious: In LightSwitch, you use built-in controls or custom controls written in Silverlight, but you do not have any chance to modify existing controls, which makes sense because of the nature of both LightSwitch and of the applications that it generates.

To complete the current discussion, focus on the UserCode folder. Each file contains method definitions for common actions, such as loading and saving data. For instance, take a look at the CreateNewCustomer.vb code file. This defines the CreateNewCustomer class, which represents the screen used to add new customers. Listing 12.1 shows what the code looks like.

Listing 12.1. The Autogenerated Code for CreateNewCustomer


Namespace LightSwitchApplication

    Public Class CreateNewCustomer

        Private Sub CreateNewCustomer_InitializeDataWorkspace _
                    (ByVal saveChangesTo As Global.System.
                     Collections.Generic.List(Of Global.
                                              Microsoft.LightSwitch.
                                              IDataService))
            ' Write your code here.
            Me.CustomerProperty = New Customer()
        End Sub

        Private Sub CreateNewCustomer_Saved()
            ' Write your code here.
            Me.Close(False)
            Application.Current.ShowDefaultScreen(Me.CustomerProperty)
        End Sub

    End Class

End Namespace


As you learned in Chapter 11, the InitializeDataWorkspace is a method called before data is loaded. In this particular case, a new instance of the Customer entity is assigned to the CustomerProperty property, which represents the customer that is being added. You also learned in Chapter 11 that Saved is a method called after the save process has completed successfully. In this case, the code closes the New Customer screen and returns control to the default screen in the application, by passing the current customer as the parameter so that the default screen focuses on that entity instance. The Client project contains some other folders, which you see empty in Solution Explorer. As was explained for the ClientGenerated project, these folders will contain items generated at compile time and that support the user interface.

Understanding Compiled Files

When you compile the project, LightSwitch generates a number of files that will be packaged for deployment. The output of this process is available under the binDebug or binRelease subfolders, depending on the configuration you chose in Visual Studio. Inside these folders, you can see additional elements:

Bin folder: This contains libraries required by the WCF RIA Services infrastructure and by the LightSwitch runtime.

Web folder: This contains the application package (.xap) and a Manifests subfolder that stores libraries required to run the application.

General-purpose files, required to support the application: Among others, Web.config (the configuration file that system administrator can edit), Silverlight.js (a JavaScript file required to host Silverlight user controls within web pages), and ClientAccessPolicy.xml (required to allow cross-domain calls).

You usually do not need to interact with the generated output because LightSwitch handles it for you, especially at the moment at which you deploy the application either as a desktop client or a web client. By the way, it can be useful to know how to find files that a LightSwitch application generates and requires, especially those files such as Web.config that can be modified by other people in your organization (such as server administrators).

Summary

Visual Studio LightSwitch is a powerful and simple-to-use environment that makes it easier to build business applications. However, behind the scenes, a lot of complex work has been done to create such a number of tool, and lots of .NET technologies have been put together to create a reliable, maintainable, and powerful architecture.

This chapter explained how LightSwitch applications are architected. You learned about the data tier, the logic tier, and the presentation tier. You saw which technologies are involved in each tier and how tiers communicate with one another. For instance, you saw how the data tier involves SQL Server, SQL Azure, and SharePoint 2010 as data storages, and you saw how the logic tier involves WCF RIA Services and the ADO.NET Entity Framework. This chapter also showed how the presentation tier involves Silverlight 4 and architectural patterns like the Model-View-ViewModel. After talking about the architecture, the discussion moved to explain how a LightSwitch solution is organized from a typical Visual Studio point of view, so you learned about the File View and then learned what projects make a LightSwitch solution on disk.

You usually do not interact directly with an application’s architecture, but some information provided in this chapter was necessary to lay some groundwork understanding of concepts covered more fully in other chapters. Beginning in the next chapter, you learn how powerful the Visual Studio environment is for LightSwitch as you analyze advanced instrumentation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.179.220