I had been running a beta of Visual Studio 2008 and when I uninstalled the beta and installed the RTM version, I found that I had no syntax highlighting or intellisense when editing config files (web.config, app.config, etc.) When I tried to change the properties in the IDE, I received An error occurred loading this property page from the tools -> options menu.
To fix the issue I ran the below command from the Visual Studio 2008 command prompt:
devenv /setup
If you need to update user's passwords that are stored in the aspnet_Membership, the below code should help you accomplish this.
If you are using the default SqlMemberShip provider for authentication, you may have a need to manually reset a user's password. In order to accomplish this, you can hash the password with a salt and then store the password and salt in the aspnet_Membership table.
string salt = GenerateSalt();
string password = EncodePassword("mypassword", 1, salt);
public string EncodePassword(string pass, int passwordFormat, string salt)
{
if (passwordFormat == 0) // MembershipPasswordFormat.Clear
return pass;
byte[] bIn = Encoding.Unicode.GetBytes(pass);
byte[] bSalt = Convert.FromBase64String(salt);
byte[] bAll = new byte[bSalt.Length + bIn.Length];
byte[] bRet = null;
System.Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);System.
Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
if (passwordFormat == 1)
{ // MembershipPasswordFormat.Hashed
HashAlgorithm s = HashAlgorithm.Create(Membership.HashAlgorithmType);
bRet = s.ComputeHash(bAll);
}
else
{
//bRet = EncryptPassword(bAll);
}
return Convert.ToBase64String(bRet);
}
private string GenerateSalt()
{
byte[] buf = new byte[SALT_SIZE_IN_BYTES];
(new RNGCryptoServiceProvider()).GetBytes(buf);
return Convert.ToBase64String(buf);
}
Once you have the salt and password, you can store them in the database.
UPDATE [aspnet_Membership] SET Password = [use the password hash from above],
PasswordSalt = [use salt from above] WHERE [UserId] = CAST('123456789...' as uniqueidentifier)
Finally you can now authenticate the user with a password of "mypassword".
If you are trying to run a .vsi file and have both VS 2005 and VS 2008 you may encounter an error that indicated the file microsoft.wizardframework could not be loaded:
could not load file or assembly 'microsoft.wizardframework
Below are the steps I followed to correct:
- Check the GAC (C:\WINDOWS\assembly) for the assembly Microsoft.WizardFramework.dll
- If it does not exist, check that it exists in C:\Program Files\Micro
soft Visual Studio 9.0\Common7\IDE\
- If it does, then open a VS command prompt and add the dll to the GAC
gacutil /i "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\Microsoft.WizardFramework.dll"
- Check for the dll in C:\WINDOWS\assembly
- You should now be able to run the VSI
If you create a named instance in SQL Server 2005 and you receive the message below you should try the following:
An error has occurred while establishing a connection to the server. When connecting to SQL Server 2005, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified) (Microsoft SQL Server, Error: -1)
1. Verify that remote connections are enabled. You can right click on the instance from SSMS and check the connections section to make sure remote connections are enabled or use the surface area configuration tool.
2. If after verifying that remote connections are enabled, you still can not connect, you should enable and start the SQL Server Browser service using the Surface Area Configuration tool. Once this service starts, you should be able to connect to the named instance(s).
I like to do a combination of unit and integration testing with my projects. When testing code that update the database you need to account for the state of your databse. If you want repeatable tests, you either need to use stubs or mocks or find a way to return your databse to the state it was in prior to your tests running.
One approach that I have been using with success is leveraging the System.Transaction name space within my tests that update the database. Below is some sample code using the NUnit testing framework. By wrapping the code that updates the database within a transaction (using (...)) the save operation is rolled back:
/// <summary>
/// Transaction is not "completed" so gets rolled back preventing
/// test data from being persisted to db.
/// </summary>
[Test]
public void Should_Insert_A_New_Info_Object_Into_DB()
{
using (TransactionScope transScope = new TransactionScope())
{
string testDateTime = DateTime.Now.ToString();
Info info = AddTestRecord(testDateTime);
Assert.IsTrue(info.Subject == "Subject: " + testDateTime);
info.Save();
Assert.IsTrue(info.Subject == "Subject: " + testDateTime);
}
}
Hope people find this technique useful.
If you receive the below error when trying to build a project that was composed using the VS 2008 Beta, you need to modify your config files.
Change the assembly version from 2.0.0.0 to
Version=3.5.0.0
Error 1 Could not load file or assembly 'System.Data.DataSetExtensions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' or one of its dependencies. The system cannot find the file specified.
This is the final part on how to implement database paging with a custom CSLA business class and SQL Server 2005. In this post I will demonstrate how to call the business object from part two from the applications presentation layer. This approach will place minimal code in the presentation layer and allows for clean seperation and the ability to write automated tests against the intended behavior.
I used a DataGrid and CSLADataSource to present the data from the list class:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="CslaDataSourceInfoList"
DataKeyNames="Id" OnRowDeleted="GridView1_RowDeleted" Width="100%" PageSize="10"
AllowPaging="True" AllowSorting="true" EmptyDataText="No Records Available."
PagerSettings-Mode="NumericFirstLast ">
<Columns>
<asp:BoundField DataField="Id" HeaderText="Id" ReadOnly="True" SortExpression="Id"
Visible="False" />
<asp:HyperLinkField HeaderText="Subject" SortExpression="Subject" DataNavigateUrlFormatString="InfoEdit.aspx?id={0}"
DataNavigateUrlFields="Id" DataTextField="Subject" ItemStyle-Width="300" />
<asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True"
SortExpression="CategoryName" ItemStyle-Width="75"/>
<asp:BoundField DataField="Description" HeaderText="Description" ReadOnly="True"
SortExpression="Description" ItemStyle-Width="200"/>
<asp:TemplateField ControlStyle-Width="50px" HeaderStyle-Width="60px" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:Button ID="btnDelete" CommandName="Delete" runat="server" Text="Delete"
OnClientClick="showConfirm(this); return false;" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<csla:CslaDataSource ID="CslaDataSourceInfoList" runat="server" TypeAssemblyName=""
TypeName="InfoTracker.InfoListItem, InfoTracker" TypeSupportsPaging="True" TypeSupportsSorting="False"
OnSelectObject="CslaDataSourceInfoList_SelectObject" OnDeleteObject="CslaDataSourceInfoList_DeleteObject">
</csla:CslaDataSource>
In the code behind, I used the SelectObject event handler to retrieve the business object and specify the paging information. Finally in the GetInfoList method I call into the business class to get the data:
protected void CslaDataSourceInfoList_SelectObject(object sender, Csla.Web.SelectObjectArgs e)
{
InfoList infoList = GetInfoList(GridView1.PageIndex, GridView1.PageSize, GetSort(GridView1.SortExpression, GridView1.SortDirection.ToString()),tbSearch.Text);
lblTotalRecords.Text = infoList.TotalRecordCount.ToString();
e.BusinessObject = infoList;
}
private InfoTracker.InfoList GetInfoList(int index, int pageSize, string sort, string searchString)
{
return InfoTracker.InfoList.GetInfoList(index, pageSize, sort, searchString);
}
Once your stored procedure is set up (see part 1) you can turn your attention to your CSLA class. The class I am focusing on here is a read only list called "InfoList" that has a child class "InfoListItem". This is a design taken directly from Rocky's book and sample application. The class is declared as follows:
public class InfoList : ReadOnlyListBase<InfoList, InfoListItem>, Csla.Core.IReportTotalRowCount
In order to set up the grid view paging I am implementing the IReportTotalRowCount which is needed to get the total records satisfying your sp query. The following is the implementation for the interface:
#region IReportTotalRowCount Members private int _totalRowCount;
/// <summary>
/// TotalRowCount - this property returns the total row count for the initial list that passed to the
/// constructor.
/// </summary>
int Csla.Core.IReportTotalRowCount.TotalRowCount
{
get { return _totalRowCount; }
}
//Property that can be used in UI to display total number of records
public int TotalRecordCount
{
get { return _totalRowCount; }
}
#endregion
Next is the code for the data access. I have created a FilteredCriteria class that will handle the fields necessary to filter the resuts for paging:
[Serializable]
private class FilteredCriteria
{
internal const int MAXROWS = int.MaxValue;
internal const string DEFAULTSORT = "Subject";
private int _pageIndex;
private int _pageSize;
private string _sort;
private string _searchString;
public string searchString
{
get { return _searchString; }
}
public string Sort
{
get { return _sort; }
}
public int PageIndex
{
get { return _pageIndex; }
}
public int PageSize
{
get { return _pageSize; }
}
public FilteredCriteria(int index, int pageSize, string sort, string searchString)
{
//this._pageIndex = GetPageIndex(index,numberOfRecords);
this._pageIndex = index;
this._pageSize = pageSize;
if (String.IsNullOrEmpty(sort))
{
this._sort = DEFAULTSORT;
}
else
{
this._sort = sort;
}
this._searchString = searchString;
}
}
Finally, the Fetch method is coded to pass in the values from the criteria class. This is just a standard sp call, the only additional code is that to get the total row count from the stored procedure and set the _totalRowCount field:
/Get total number of records for GridView paging
dr.NextResult();
dr.Read();
_totalRowCount = dr.GetInt32("TotalRowCount");
That completes the code required for the business class. The final part is setting up the UI to leverage the pagin logic. I will demonstrate this in Part 3.
I have encountered an issue when running the database tuning advisor against SQL Server 2005 that if the advisor errors out while performing its analysis, it will leave the hypothetical stats/indexes in your database. These should be removed as they may result in additional processing for SQL Server.
The below statement will generate the scripts to drop these stats/indexes.
SELECT 'drop statistics [' + object_name(i.[object_id]) + '].['+ i.[name] +
']'
FROM sys.stats as i
WHERE OBJECTPROPERTY(i.[object_id],'IsUserTable') = 1 AND i.[name] LIKE
'_dta%'
ORDER BY i.name
If you receive the below error change the script to drop index:
Msg 3739, Level 11, State 1, Line 1
Cannot DROP the index because it is not a statistics collection.