A lot of times LightSwitch customers will run into an error in the product and post a question on the forums but the LightSwitch development team needs more information about the error. This blog post describes how you can help the LightSwitch development team determine the source of those kinds of errors you may encounter. Unexpected errors (or exceptions as we call them) within the product can manifest themselves either by an error message, a crash, lack of response (hang), or when an expected behavior doesn’t occur. By providing an exception stack trace in your forum posts, it provides valuable information to the development team and helps us help you.
Before beginning the process of debugging, you’ll want to follow the necessary steps in Visual Studio or your LightSwitch app that reproduce the exception but stopping just prior to the last step that actually causes the exception. If you attach and debug prior to all these steps you may encounter a bunch of exceptions that are unrelated to the issue or performance of the process may be degraded enough that will make you annoyed. So just get to the point right before the exception occurs and then follow the instructions below.
Attaching to the Process
The next step is to identify which process to debug. This handy flowchart can help you determine which process you should be attached to in order to retrieve the exception stack trace:
Note that if the issue you’re investigating occurs within your running application while you are running it from within Visual Studio via F5, Visual Studio will automatically be attached to both the client and server processes. In all other cases, you’ll need to manually attach to the process. Of course, if the issue occurs within Visual Studio itself, you’ll need to launch another instance of Visual Studio to attach to the original instance since a given VS process cannot attach to itself.
To manually attach to the process, go to “Tools –> Attach to Process…” in Visual Studio.
In the dialog that opens, select the name of the process as indicated by the flowchart above. This will only allow you to attach to the process if it is running on the local machine. If you want to attach to a remote process (for example, attach to the IIS process on a remote machine), you’ll need to follow the instructions in this MSDN article: Setup Remote Debugging. If the process is hosted on a machine you don’t have access to, like Azure, you won’t be able to follow the debugging instructions described in this post. Instead, you’ll need to use diagnostic tracing as described in this post: Diagnosing Problems in a Deployed 3-Tier LightSwitch Application.
Configure for Debugging
Now that Visual Studio is attached to the process, it needs to be properly configured to break on exceptions in order to see the stack trace of the exceptions.
The first thing to do is to ensure that VS is configured to debug code that you don’t own. Visual Studio has a feature called “Enable Just My Code” that is enabled by default which prevents you from debugging code that isn’t yours. To turn it off, follow these steps:
- Go to Tools –> Options.
- If necessary, click the “Show all settings” check box at the bottom of the dialog if the “Debugging” node doesn’t show up in the tree.
- In the Options dialog, navigate to Debugging –> General in the tree.
- Ensure that the “Enable Just My Code” check box is not checked.
This will break on all exceptions of that category. A lot of times there will be exceptions thrown that are irrelevant to the actual issue. These are normal and are handled by the LightSwitch code. If you know the specific exception type that you want to break on, you can configure this dialog to break only on that exception type by drilling into the Common Language Runtime Exceptions node and finding the exception or by clicking the Find button and searching for it that way.
Reproduce the Exception
You’re now ready to do the final step in Visual Studio or your LightSwitch app that actually causes the exception to occur. Once you do that, you’ll see an exception message appear in Visual Studio that looks like this:
Or like this if you’re debugging an HTML client:
As mentioned earlier, there can be exceptions that are thrown that are properly handled by LightSwitch and irrelevant to your actual issue. You’ll want to work with someone on the LightSwitch development team via the forums or e-mail to determine which exception is the one that is relevant. If you know the exception is not relevant, you can continue execution by clicking the “Continue” button.
Collect Exception Information
Paste that text along with the other exception detail you collected into the text editor. You’ve now collected enough information to pass along to the LightSwitch development team. Of course, along with the exception information, you should still describe the set of steps that reproduce the issue.
Then follow the steps to reproduce the issue again. This time it will provide the friendly function names in the stack trace which is much more useful. Be sure to revert your changes to the default.htm file when you’re done debugging.
When you enable Windows or Forms authentication in a LightSwitch app, a set of SQL tables are used to store the user and role data. But did you know that you can customize how and where LightSwitch gets the user and role data? You don’t have to use the default set of SQL tables that LightSwitch generates in its intrinsic database. This post describes in detail how you can go about customizing the management of users and roles in your LightSwitch app.
LightSwitch makes use of many APIs within ASP.NET. Among those are the provider classes for user and role management. You can learn more about these providers and their associated APIs on MSDN:
The great thing about this provider model is that it is configurable outside of source code. You can configure your app, defining which providers to use and their settings, by editing your web.config file. And since all providers have a common set of base classes, LightSwitch doesn’t need to know which provider type it’s using; the web.config file and ASP.NET dictate which providers LightSwitch uses.
There are a set of base provider classes that we’re interested in here:
- System.Web.Security.MembershipProvider: This class defines the interface used to access user information such as the login name and password of a user.
- System.Web.Security.RoleProvider: This class defines the interface used to access role information (e.g. SalesPerson or Manager).
- System.Web.Profile.ProfileProvider: This class defines the interface used to access custom user-specific data. This is data that the application itself wants to track along with the user. ASP.NET doesn’t track any data of its own here; this is all application-specific.
LightSwitch makes use of each of these provider types and each of them can have a custom implementation that LightSwitch will invoke.
How LightSwitch Works
Before I get into how you can customize the user and role management, I wanted to describe how LightSwitch configures things. Understanding this will allow you to be better informed when time comes to do the customizing.
LightSwitch makes use of the following implementations of the previously mentioned base provider classes:
Each of these classes are implementations of the base provider interface such that they make use of a set of SQL tables to store and retrieve their respective data. For example, the SqlMembershipProvider implementation makes use of a combination of the aspnet_Membership and aspnet_Users tables contained in the LightSwitch intrinsic database.
If you enable authentication in your LightSwitch app, save, and open your web.config file, you’ll see how LightSwitch configures the use of these providers. For more information on configuring authentication in your LightSwitch app, see LightSwitch Authentication and Authorization.
LightSwitch uses the membership provider to store the users that have access to the application. When you log into a LightSwitch app with your username and password, for example, LightSwitch makes use of the membership provider to ensure 1) you exist as an application user and 2) your password matches.
Line #1: The membership element configures the ASP.NET membership management. It contains a defaultProvider attribute which references the name of membership provider defined below that should be used by default.
Line #2: The providers element defines the collection of membership providers and their configurations. Obviously, this implies you can define multiple membership providers in this section which ASP.NET supports. LightSwitch does not support multiple providers. Only the membership provider marked as the default (via the defaultProvider attribute on the membership element) will be used.
Line #3: The clear element simply ensures that the providers element collection is cleared prior to adding the membership provider. This isn’t strictly necessary but it ensures that there are no extraneous providers included in the collection that may have been added by fancy things like web.config inheritance.
Line #4: The add element defines which membership provider should be added to the collection and how it should be configured. It has several attributes that are configured by LightSwitch by default.
Line #5: The name attribute is an identifier for the provider within the web.config so that it can be referenced as the default provider.
Line #6: The type attribute is the .NET type name of the membership provider.
Line #7: The connectionStringName attribute is the name of the connection string that this membership provider should used when accessing the database tables.
Line #8: The applicationName attribute is the name of the application with which to associate the membership data. ASP.NET’s implementation of the SqlMembershipProvider allows you to have multiple applications all share the same database but partition, or share depending on how things are configured, the user information between those applications. Applications that have membership providers configured with the same application name will share the membership data; if they are different application names, the membership data will be separate between the apps.
Line #9: The requiresUniqueEmail attribute indicates whether a unique e-mail address is required for the user. By default, SqlMembershipProvider sets this value to true. LightSwitch doesn’t collect an e-mail address for a user, so this is set to false.
Line #10: The requiresQuestionAndAnswer attribute indicates whether a password question/answer pair is required for the user to allow for password reset and retrieval. By default, SqlMembershipProvider sets this to true. Again, since LightSwitch doesn’t use e-mail addresses, this is set to false.
The snippet above shows how LightSwitch configures the membership provider when Windows authentication is being used. For Windows authentication, LightSwitch continues to use SQL-based storage to track which Windows users have access. Even in the case where the app has been configured to allow access to any authenticated Windows user, LightSwitch will still make use of this storage to track which app-defined roles are assigned to any of those users. LightSwitch makes use of a custom implementation membership provider, derived from SqlMembershipProvider that adds some extra logic specific to handling Windows users and also correctly defaults most of the settings which is why you don’t see the requiresUniqueEmail and requiresQuestionAndAnswer settings here. Note that the type attribute is set using an assembly-qualified type name since this is a custom type that is not defined by ASP.NET.
LightSwitch uses the role provider to store the roles that have been defined within the app and to map those roles to users.
The snippet above shows how LightSwitch configures the role provider when any authentication is being used. It’s pretty much the same structure as the membership snippet. The only real difference is that the roleManager element has an enabled attribute. That attribute needs to be set to true in order to enable the use of roles in the ASP.NET API. Just like with the membership provider, LightSwitch only makes use of the role provider that is defined as the default provider.
LightSwitch uses the profile provider to store some extra information about the user in the case where Forms authentication is being used. In general, the profile provider can be used to track other custom data for a user that is not represented in the membership provider/SQL schema. In the case of LightSwitch, it keeps track of the full name (display name) of a user, so the profile provider is configured to store this data.
The snippet above shows how LightSwitch configures the profile provider. It follows the same structure as the other two snippets except for the properties child element. The profile element contains a properties child element that allows a developer to define the names of properties that should be associated with each user. In the case of LightSwitch, it defines a FullName property here. Just as with the other providers, LightSwitch only makes use of the profile provider that is defined as the default provider.
ASP.NET has the concepts of users (membership), roles, and profiles of which LightSwitch makes use. But ASP.NET does not have the concept of permissions, while LightSwitch does. So LightSwitch defines its own table, named RolePermissions, within the intrinsic database to map roles and permissions as that is how permissions are assigned in LightSwitch apps. There does not exist an ASP.NET provider that accesses this table; LightSwitch uses technology outside of ASP.NET to access this table. For this reason, the storage of role-permission mappings is not customizable. You can customize how and where roles are stored but when LightSwitch needs to map a permission to that role, it always stores that mapping information in the RolePermissions table.
ASP.NET Provider Customization
Now that you understand how LightSwitch configures the ASP.NET providers it uses, let’s get into how you can go about customizing them. There are a few ways you can go about customizing the providers:
- Configure provider properties via the web.config
- Replace with an existing provider class implementation
- Define a custom provider
Configuring Provider Properties
The easiest but least flexible way to customize an ASP.NET provider is to configure its properties within the web.config file. For example, if you want to change the maximum number of password attempts allowed for a user from the default of 5 to 10, you would set the maxInvalidPasswordAttempts attribute in the add element of the default membership provider configured by LightSwitch (see line #9):
If you had a separate database location where your ASP.NET users and roles were stored, you could configure your providers to use that database by simply adding a connection string to your web.config file and setting the connectionStringName attributes of your providers to reference that connection string.
Consult the documentation of the provider type in order to determine which properties it defines that are configurable through the web.config file.
Replacing a Provider
If there already exists an implementation of an ASP.NET provider different than the default one used by LightSwitch that you want to use, you can simply configure the web.config file to reference that provider type and LightSwitch will make use of it. To do this, follow these steps:
- Add a reference to the assembly that contains the provider type you want to use from your LightSwitch app’s Server project, being sure to set the “Copy Local” property to true for the assembly reference. This will ensure that the assembly will be copied to the runtime environment.
- Set the add element’s type attribute for that provider to be the assembly-qualified type name of the provider type you want to use. For example, see line #6:
Defining a Custom Provider
In order to write your own custom provider, you define a class that inherits from the appropriate provider base type (System.Web.Security.MembershipProvider, System.Web.Security.RoleProvider, or System.Web.Profile.ProfileProvider) and update the web.config just as was explained in the previous “Replacing a Provider” section.
MSDN provides good documentation on how to go about implementing a custom provider:
These providers can contain a large number of members that must be implemented in the derived class. In the case of LightSwitch’s usage of these providers, not all the members need to be fully implemented. Here’s a list of provider members which LightSwitch uses and, thus, must be implemented (as opposed to just throwing a NotImplementedException):
- MaxInvalidPasswordAttempts [Forms authentication only]
- MinRequiredNonAlphanumericCharacters [Forms authentication only]
- MinRequiredPasswordLength [Forms authentication only]
- PasswordStrengthRegularExpression [Forms authentication only]
- ChangePassword [Forms authentication only]
- GetUser(string, bool)
- ResetPassword [Forms authentication only, “answer” argument will always be null]
- ValidateUser [Forms authentication only]
- ProfileProvider [Forms authentication only]
It was important within the design of LightSwitch that existing technologies were used where appropriate. In the case of user and role management, ASP.NET already provided a mechanism for this, so we didn’t reinvent the wheel and instead built LightSwitch on top of those APIs. This allows LightSwitch to be customized in a way that is not unique to LightSwitch but is a familiar experience for ASP.NET developers.
I encourage you to read the MSDN articles that are linked from this blog post. You can learn a lot of extra information on the APIs and provider customization from those articles.
Please let me know if you have any questions or run into any issues.
In this blog post, I’m going to describe how LightSwitch developers can programmatically access the security data contained in a LightSwitch application. Having access to this data is useful for any number of reasons. For example, you can use this API to programmatically add new users to the application.
This data is exposed in code as entities, just like with your own custom data. So it’s an easy and familiar API to work with. And it’s available from both client or server code.
First I’d like to describe the security data model. Here’s a UML diagram describing the service, entities, and supporting classes:
This is the data service class that provides access to the security entities as well as a few security-related methods. It is available from your DataWorkspace object via its SecurityData property.
SecurityData is a LightSwitch data service and behaves in the same way as the data service that gets generated when you add a data source to LightSwitch. It exposes the same type of query and save methods. It just operates on the built-in security entities instead of your entities.
Some important notes regarding having access to your application’s security data:
In a running LightSwitch application, users which do not have the SecurityAdministration permission are only allowed to read security data; they cannot insert, update, or delete it. In addition, those users are only able to read security data that is relevant to themselves. So if a user named Bob, who does not have the SecurityAdministration permission, queries the UserRegistrations entity set, he will only see a UserRegistration with the name of Bob. He will not be able to see that there also exists a UserRegistration with the name of Kim since he is not logged in as Kim. Similarly with roles, if Bob queries the Roles entity set, he can see that a role named SalesPerson exists because he is assigned to that role. But he cannot see that a role named Manager exists because he is not assigned to that role.
Users which do have the SecurityAdministration permission are not restricted in their access to security data. They are able to read all stored security data and have the ability to modify it.
In addition to entity sets, SecurityData also exposes a few useful methods:
This method allows a user to change their own password. They need to supply their old password in order to do so. If the oldPassword parameter doesn’t match the current password or the new password doesn’t conform to the password requirements, an exception will be thrown. This method is only relevant when Forms authentication is being used. This method can be called by any user; they do not require the SecurityAdministration permission.
This method validates and resolve a Windows account name into a normalized value and retrieve the full name (display name) of that user. As an example, let’s say that you passed “firstname.lastname@example.org” as the parameter to this operation, it would return back a WindowsUserInfo object with the FullName property set to “Kim Abercrombie” and the NormalizedUserName property set to “contoso\kim”. If the service is unable to resolve the username parameter to a known account, an exception will be thrown. This method is only relevant when Windows authentication is being used. This method can only be called by users that have the SecurityAdministration permission.
This method checks whether the supplied password meets the password requirements of the application. This happens automatically when attempting to add a new user or updating their password but this method allows the caller to preemptively check the password which can be useful for validation UI purposes. This method is only relevant when Forms authentication is being used. This method can only be called by users that have the SecurityAdministration permission.
The UserRegistration entity represents a user that has access to the application. (In VS 11, it can also represent an Active Directory security group when Windows authentication is being used). When using Forms authentication, all the UserRegistration properties (UserName, FullName, Password) are required. When using Windows authentication, only the UserName property is required; FullName is populated dynamically based on the UserName and Password is irrelevant.
Here is some example code using the UserRegistration entity:
UserRegistration user = this.DataWorkspace.SecurityData.UserRegistrations.AddNew();
user.UserName = "contoso\\kim";
// Create a new UserRegistration (Forms authentication)
UserRegistration user = this.DataWorkspace.SecurityData.UserRegistrations.AddNew();
user.UserName = "kim_abercrombie";
user.FullName = "Kim Abercrombie";
user.Password = "mysecretpassword!";
// Iterate through all UserRegistrations
foreach (UserRegistration user in this.DataWorkspace.SecurityData.UserRegistrations)
// Find a specific UserRegistration (Windows authentication)
UserRegistration user = this.DataWorkspace.SecurityData.UserRegistrations_Single("contoso\\kim");
// Find a specific UserRegistration (Forms authentication)
UserRegistration user = this.DataWorkspace.SecurityData.UserRegistrations_Single("kim_abercrombie");
Dim user As UserRegistration = Me.DataWorkspace.SecurityData.UserRegistrations.AddNew()
user.UserName = "contoso\\kim"
' Create a new UserRegistration (Forms authentication)
Dim user As UserRegistration = Me.DataWorkspace.SecurityData.UserRegistrations.AddNew()
user.UserName = "kim_abercrombie"
user.FullName = "Kim Abercrombie"
user.Password = "mysecretpassword!"
' Iterate through all UserRegistrations
For Each user As UserRegistration In Me.DataWorkspace.SecurityData.UserRegistrations
' Find a specific UserRegistration (Windows authentication)
Dim user As UserRegistration = Me.DataWorkspace.SecurityData.UserRegistrations_Single("contoso\\kim")
' Find a specific UserRegistration (Forms authentication)
Dim user As UserRegistration = Me.DataWorkspace.SecurityData.UserRegistrations_Single("kim_abercrombie")
The Role entity represents a grouping of users with common characteristics. Examples of roles in an application include “Sales Person” and “Manager”. You can then configure your application security around these roles.
Here is some example code using the Role entity:
Role role = this.DataWorkspace.SecurityData.Roles.AddNew();
role.Name = "Manager";
// Iterate through all Roles
foreach (Role role in this.DataWorkspace.SecurityData.Roles)
// Find a specific role
Role role = this.DataWorkspace.SecurityData.Roles_Single("Manager");
Dim role As Role = Me.DataWorkspace.SecurityData.Roles.AddNew()
role.Name = "Manager"
' Iterate through all Roles
For Each role As Role In Me.DataWorkspace.SecurityData.Roles
' Find a specific role
Dim role As Role = Me.DataWorkspace.SecurityData.Roles_Single("Manager")