Online friends you have never met (not even online)
June 27, 2008 Leave a comment
Greg
Just another WordPress.com site
June 27, 2008 Leave a comment
June 27, 2008 Leave a comment
Due to the other projects we have this team on, I ended up doing it myself.
NOTE: This application has to get tings off of the querystring, at least for now, due to constraints in the specifications. It will likely morph into a cleaner FORM post in the future.
The original code had the following line:
var params:String = Application.application.parameters.params;
Here is is in context:
public function initComp():void {
var params:String = Application.application.parameters.params;
if (params.length > 0) {
readParams(params);
currentState = "showMap";
mapCont.addMapSource("1" , "lot1.swf", "Lot – 1", "Description for lot 1",
new mapPoint(33.934932, -84.174615), new mapPoint(33.930329, -84.16698),
980, 634);
mapCont.addMapSource("2" , "lot2.swf", "Lot – 2", "Description for lot 2",
new mapPoint(32.847887, -96.865415), new mapPoint(32.84229, -96.857924),
863, 767);
}
zoomBar.value = 1;
}
This routine is called from the mx:Application tag, which is fairly straightforward Flex. I am fairly certain I have some Flex Framework readers out here that can help me with this one. The line var params:String = Application.application.parameters.params; was not working on my machine. I kept getting a null instead of the value. When I install the application they compiled it works. I did not have a project file, if FlexBuilder creates one, so I did not have any compilation properties, if there are any. Enough of this aside; just ping me if you have an answer.
I went out and found that you can pull from JavaScript by working through a few hoops. First, I needed the routine to grab the querystring, which I found here. Here is the code in the JavaScript:
<script language="JavaScript" type="text/javascript">
var jsReady = false;
function isReady() {
return jsReady;
}
function pageInit() {
jsReady = true;
}
function GetQueryString()
{
var s = window.location.search.substring(1);
return s;
}
</script>
…
<body scroll="no" onLoad="pageInit();">
Easy enough. I am not sure I like search.substring(1), as it is not explicit, but it works fine. Now to my Flex file. First I need an import to use the libraries that pull from JavaScript in the page.
import flash.external.ExternalInterface;
I now needed some code to call the JavaScript routine in action script. The initComp() routine was refactored to avoid having all of the load code. The original is shown above.
public function initComp():void
{
//Pull the parameters from the querystring
pullParameters();
InitConfigLoader();
}
And here is the refactored routines to pull parameters:
private function pullParameters()
{
params = Application.application.parameters.params;
if(params == null)
{
params = EnsureParamsPulledFromQueryString(params);
}
}
private function EnsureParamsPulledFromQueryString(params:String):String
{
trace("params are null");
//Have to run JavaScript for QueryString
//BABELFISH: Tienen que funcionar el Javascript para QueryString
var isAvailable:Boolean = ExternalInterface.available;
//TODO: This should use a timer?
if(isAvailable)
{
if(checkJavaScriptReady())
{
params = ExternalInterface.call("GetQueryString");
}
}
}
The magic is in the second routine, which fires off the GetQueryString method in JavaScript. Pretty slick, eh? If you want JavaScript to call into Flex, you also need to set up a callback method, but that is beyond what we are doing right now. Here is what happens.
Now, this was not good enough for me, as the querystring was in this format:
index.aspx?000000001^vehicle1|000000002^vehicle2|000000003^vehicle3|000000004^vehicle4|000000005^vehicle5|000000006^vehicle6
NOTE: The index.aspx is merely a container at this time. That will change in future iterations.
I am not fond of non-delimited querystring values, as it leaves me no way to add addtional values. As this is not deprecated in the code, I had to leave this alone. The first condition I coded was the normal multi-parameter method of pulling:
if(params.search("&") > 0)
{
//split the string
//BABELFISH: parta la secuencia
var splitQueryString:Array = params.split("&");
//Find the correct variable and get rid of ‘vehicles’
//BABELFISH: Encuentre el variable correcto y líbrese de ‘ vehicles’
var i:uint;
for(i=0;1<splitQueryString.length;i++)
{
if(splitQueryString[i].substr(0,8) == "vehicles")
{
params = splitQueryString[i].toString().replace("vehicles=", "");
break;
}
}
}
I am fairly certain there is a better syntax here (I could write it shorter in C#, at least), so I am going to learn more Flex to see if I can shorten this up a bit. I am asking here if the string has an ampersand (&), which indicates more than one querystring argument. I then look for the string with vehicles, split it and grab the right hand part of the string. The replace is fairly crude, but it works. This covers this type of string:
index.aspx?debug=true&vehicles=000000001^vehicle1|000000002^vehicle2|000000003^vehicle3|000000004^vehicle4|000000005^vehicle5|000000006^vehicle6
Tested and it works. But what if there is only one argument. It will not find the ampersand, but it will find an equals sign (=). So, I add this condition.
else if(params.search("=") > 0)
{
//See if this is vehicles or something else
//BABELFISH: Vea si éste es vehículos o algo más
if(params.substr(0,8) == "vehicles")
{
params=params.replace("vehicles=", "");
}
else
{
params = "";
}
}
I have now got something that will pull the string when it is correctly set up as key/value. I also avoid the wrong key, if the single key does not contain the information I want. Any of these URLs yield the correct answer:
index.aspx?000000001^vehicle1|000000002^vehicle2|000000003^vehicle3|000000004^vehicle4|000000005^vehicle5|000000006^vehicle6
index.aspx?vehicles=000000001^vehicle1|000000002^vehicle2|000000003^vehicle3|000000004^vehicle4|000000005^vehicle5|000000006^vehicle6
index.aspx?debug=true&vehicles=000000001^vehicle1|000000002^vehicle2|000000003^vehicle3|000000004^vehicle4|000000005^vehicle5|000000006^vehicle6
And, I have been pessimistic enough that these URLs do not crash anything:
index.aspx
index.aspx?debug=true
One problem solved.
NOTE: As I went to "press" on this, I found that this type of URL could crash the application, so I have to refactor again.
index.aspx?debug=true&
Coming from an ASP.NET world, I use configuration files all of the time. While you cannot change elements on the fly and have the application continue to run without rebooting (not completely true, as you can have your own "config" files), you can customize installs on different machines. What this means, to me, is I can run a variety of like web applications with different skins (configured) and even turn on an off features in config. This is critical to my work, as it stands today, as we produce customized websites with identical funcationality (at least most of it). One engine, multiple sites is a much better option than multiple engines, multiple sites — at least from a maintenance standpoint.
But I digress, as we all know how important configuration is.
As far as I can tell config.xml, the Flex config file, compiles into the application. It is also copied to the output directory, which I do not understand, as I see no way to pull from it at run time. If somebody knows a built in way, let me know. Here is the solution I employed.
I found a blog entry from Ryan Guill which details a component called configLoader (you can find the blog entry here). I included the componet inside my Flex project under the com/util package (create folders and you create package names). Pulling with this is fairly simple. First, you have to pull in import statements to include the package.
import com.util.ConfigLoader;
import com.util.ConfigLoaderEvent;
Next, I have my Init for the config loader.
private function InitConfigLoader()
{
configLoader = new ConfigLoader("config.xml");
configLoader.addEventListener(ConfigLoaderEvent.COMPLETE,configLoader_complete_handler);
configLoader.addEventListener(ConfigLoaderEvent.FAULT,configLoader_fault_handler);
configLoader.load();
}
Pretty standard. Load the file and careate events to listen to events. This is also straight from the blog page. When it finishes loading, I end up in the routine configLoader_complete_handler, which looks like this:
public function configLoader_complete_handler ( e:ConfigLoaderEvent ) : void
{
configXML = e.data;
useConfig(configXML);
}
And, I procede to tear out the XML in the useConfig routine.
public function useConfig( xml:XML ) : void
{
var i:uint;
for(i=0;i<xml.maps.map.length();i++)
{
var mapId:String = xml.maps.map[i].ID;
var source:String = xml.maps.map[i].mapSource;
var mapName:String = xml.maps.map[i].name;
var mapDescription:String = xml.maps.map[i].description;
var ulMapPoint:mapPoint = new mapPoint(xml.maps.map[i].upperLeftLat, xml.maps.map[i].upperLeftLong);
var lrMapPoint:mapPoint = new mapPoint(xml.maps.map[i].lowerRightLat, xml.maps.map[i].lowerRightLong);
var x:Number = xml.maps.map[i].imageX;
var y:Number = xml.maps.map[i].imageY;
mapCont.addMapSource(mapId, source, mapName, mapDescription, ulMapPoint, lrMapPoint, x, y);
}
}
The XML for this routine is here:
<config>
<webServicePath>http://localhost/MyWebService/webServiceModule.aspx</webServicePath>
<maps>
<map>
<ID>1</ID>
<name>Lot – 1</name>
<imageX>600</imageX>
<imageY>400</imageY>
<description>Lot 1 Description</description>
<mapSource>lot1.swf</mapSource>
<upperLeftLat>33.934932</upperLeftLat>
<upperLeftLong>-84.174615</upperLeftLong>
<lowerRightLat>33.930329</lowerRightLat>
<lowerRightLong>-84.16698</lowerRightLong>
</map>
<map>
<ID>2</ID>
<name>Lot – 2</name>
<imageX>600</imageX>
<imageY>400</imageY>
<description>Lot 3 Description</description>
<mapSource>lot2.swf</mapSource>
<upperLeftLat>32.847887</upperLeftLat>
<upperLeftLong>-96.865415</upperLeftLong>
<lowerRightLat>32.84229</lowerRightLat>
<lowerRightLong>-96.857924</lowerRightLong>
</map>
</maps>
<screen width="500" height="350" />
</config>
Pulling the web service to get data is also easy, so I now have an application that can run on any system, with multiple maps configured. There are still a few things I need to do, but it is much better than it was a few hours ago. I also refactored the web service into testable libraries, which I briefly covered in my last blog entry.
Peace and Grace,
Greg
June 20, 2008 Leave a comment
foreach (string key in actual.Keys)
Private Shared Function CreateDictionary (ByVal str As String) _
As Dictionary(Of String, String)
‘Using Generic Dictionary to make things easier (.NET 3.5)
‘BABELFISH: Usando el diccionario genérico para hacer cosas más fáciles (.NET 3.5)
Dim dict As New Dictionary(Of String, String)
‘Array desde la cadena de Esns
‘ENGLISH: Array from the chain of Esns
Dim arrEsn As String() = str.Split("|")
‘Add all items to a reusable dictionary
‘BABELFISH: Agregue todos los artículos a un diccionario reutilizable
For i As Int32 = 0 To arrEsn.Length – 1
‘Descompongo clave, valor
‘*** Divide Hash into ESN and description
Dim subArr As String() = arrEsn(i).Split("^")
‘Adding ESN and Description
‘BABELFISH: Adición de ESN y de la descripción
dict.Add(subArr(0), subArr(1))
Next
Return dict
End Function
In order to facilitate, I am going to move the ESN string to a TestHelper (not the actual name, but it is the purpose), so my test changes to this:
[TestMethod()]
[DeploymentItem("CompanyName.Client.dll")]
public void CreateDictionaryTest()
{
string stringtoBreakUp = TestHelper.GetEsnString();
Dictionary<string, string> expected =
TestHelper.GetExpectedDictionaryForTest();
Dictionary<string, string> actual =
DataBaseHelper_Accessor.CreateDictionary(stringtoBreakUp );
foreach (string key in actual.Keys)
{
Assert.AreEqual(expected[key], actual[key], "Difference in value for key " + key);
}
}
The change here is highlighted. Why move this to another class? I will need this same information over and over again. Looking at the test that gets a list from the database (the integration test), you see this:
string Esns = TestHelper.GetEsnString();
DataSet expected = TestHelper.GetDataSetOfVehiclesForTest();
June 19, 2008 1 Comment
The question came into the Microsoft Expression Web forum. I would have answered it there, but two things happened:
Let’s ignore #1 for now, as it is out of my control and focus on #2. While this post was in Expression, it is a problem that everyone who attempts to consume XML will run into. Here is the question, so you have the context:
I have an Access 2007 Database and I want to export data in an XML format for inclusion into my web site. While Access will export the file, it is in a format EW does not accept when I try to create a Grid View.
<?xml version="1.0" encoding="UTF-8" ?>
- <dataroot xmlns:od="urn:schemas-microsoft-com:officedata" generated="2008-06-18T11:20:50">
- <WebMachines>
<MachineID>6407</MachineID>
<TypeMachine>AUTO SCREW MACHINES, SWISS, CNC-MILL & SUB SPDL</TypeMachine>
<Year>2000</Year>
<Manufacturer>Nomura</Manufacturer>
<Model>NN-13TB</Model>
<Control>Mitsubishi Meldas 520L</Control>
<Condition>Very Good</Condition>
<List>2000 Nomura NN-13TB Mitsubishi Meldas 520L</List>
<Code>A8175MS</Code>
<Featured>0</Featured>
</WebMachines>
Now the XML data that is in the Sample file Members.xml works fine. I guess I am confused why EW will not accept an XML file from an up-to-date version on Access 2007???
<members>
<member isactive="true" groupcolor="#FFCC3333" name="Tom Jackson" datejoined="10/06/2006" />
<member isactive="false" groupcolor="#FFA4AB28" name="Stacey Footheart" datejoined="11/10/2006" />
<member isactive="true" groupcolor="#FFA6D877" name="Jack Herdin" datejoined="6/10/2006" />
<member isactive="true" groupcolor="#FF138C48" name="Kim Getruds" datejoined="4/15/2006" />
<member isactive="false" groupcolor="#FF5FADB9" name="Larry Nusom" datejoined="7/12/2006" />
<member isactive="true" groupcolor="#FF3B1076" name="Gary Heart" datejoined="9/21/2006" />
</members>
Is there anyway to export data from a Access 2007 database that EW will accept cleanly? I have tried every possible way to export an XML file format that EW will accept. I thoughts these programs were suppose to work with each other cleanly?
I will show two ways to skin this cat. Before doing that, we need to clean up the XML a bit. First, all ampersands need to be convered to &. The same is true if there is any other reserved word. The second is to close out the </dataroot>. And the third is to get rid of the hyphens (I assume it was copied out of Internet Exploder.
<?xml version="1.0" encoding="UTF-8" ?>
<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" generated="2008-06-18T11:20:50">
<WebMachines>
<MachineID>6407</MachineID>
<TypeMachine>AUTO SCREW MACHINES, SWISS, CNC-MILL & SUB SPDL</TypeMachine>
<Year>2000</Year>
<Manufacturer>Nomura</Manufacturer>
<Model>NN-13TB</Model>
<Control>Mitsubishi Meldas 520L</Control>
<Condition>Very Good</Condition>
<List>2000 Nomura NN-13TB Mitsubishi Meldas 520L</List>
<Code>A8175MS</Code>
<Featured>0</Featured>
</WebMachines>
</dataroot>
Our first method of solving this is the ASP.NET XML control. Since the person was attempting to do this in Expression, let’s start there.
If you have a single WebMachines tag, you end up with a vertical display of the column nodes. If you have more than one, you end up with a grid. Fairly simple stuff. The XSL does all of the dirty work and you can edit the XSL file created or use the presets.
This is a bit more involved, but still fairly simple. As the XML is not using attributes (the default for auto generated columns), you will have to tell how to render the GridView.
First, drag an XmlDataSource control on the page. Browse for the xml file and select it for the first text box. Do not fill the XSLT textbox (2nd down), but do set the XPATH to /dataroot/WebMachines.
Then, drag a GridView on the page and set it to the XmlDataSource. You then will have to add templated columns and drag a label into each one. Here is the first column (MachineID):
<asp:TemplateField HeaderText="ID">
<ItemTemplate>
<asp:Label runat="server" ID="lblMachineID" Text=’<%#XPath("MachineID")%>’/>
</ItemTemplate>
</asp:TemplateField>
The XPATH is the key here when you are not using attributes. The complete source of the page is as follows, although I am only showing a few of the "columns" in the XML file.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ Page Language="C#" %>
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>Untitled 1</title>
</head>
<body>
<form id="form1" runat="server">
<asp:xmldatasource id="XmlDataSource1" runat="server" datafile="Test.xml" xpath="/dataroot/WebMachines">
</asp:xmldatasource>
<br />
<asp:gridview id="GridView1" runat="server" datasourceid="XmlDataSource1" autogeneratecolumns="false">
<Columns>
<asp:TemplateField HeaderText="ID">
<ItemTemplate>
<asp:Label runat="server" ID="lblMachineID" Text=’<%#XPath("MachineID")%>’/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Machine ID">
<ItemTemplate>
<asp:Label runat="server" ID="lblTypeMachine" Text=’<%#XPath("TypeMachine")%>’/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Year">
<ItemTemplate>
<asp:Label runat="server" ID="lblYear" Text=’<%#XPath("Year")%>’/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Manufacturer">
<ItemTemplate>
<asp:Label runat="server" ID="lblManufacturer" Text=’<%#XPath("Manufacturer")%>’/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Model">
<ItemTemplate>
<asp:Label runat="server" ID="lblModel" Text=’<%#XPath("Model")%>’/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:gridview>
</form>
</body>
</html>
Wasn’t that quite easy? Of course, the tools do not do all of this for you, which may be "problematic" for some, but the syntax is still completely declarative.
Peace and Grace,
Greg
June 13, 2008 4 Comments
return View();
Server Error in ‘/’ Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Home/SimpleSubmit
Version Information: Microsoft .NET Framework Version:2.0.50727.1434; ASP.NET Version:2.0.50727.1434
return View("Index");
This at least keeps things a bit sane, but it is not completely to my liking. How about if I control the class that feeds the page? First, we code a simple class:
public TestClass(string title, string message, string alertMessage)
June 12, 2008 Leave a comment
<script type="text/javascript">
/* <![CDATA[ */
function starterKit_mvc_membership_validateChangePassword()
{
var pwd_minChars = <% =(int)ViewData["MinimumPasswordLength"] %>;
public class FormsAuthenticationController
: StarterKits.Mvc.Membership.Controllers.BaseFormsAuthenticationController
#2 will be easier, but I need to solve #1 regardless to use the tiled maps (MapQuest currently) in MVC. An injection type model is easier to debug, as all of the code is server side until it is placed on the page. It does lead to having to test the UI to get the bits down and completely debug the solution, but a view of tags and code leads to the same issue. At least with an injection of client side code model, I can debug the JavaScript separately from the code that builds it.
Peace and Grace,
Greg
June 11, 2008 Leave a comment
namespace MvcMembership
{
public partial class test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
TryDatabaseConnection();
TryMembership();
}
private void TryMembership()
{
try
{
Response.Write(string.Format("App name: {0}<br/>",Membership.Provider.ApplicationName));
MembershipUser user = Membership.GetUser();
Response.Write("Success on Membership<br/>");
}
catch (Exception ex)
{
Response.Write("Failed on Membership<br/>");
Response.Write(string.Format("Error Type: {0}<br/>", ex.GetType().ToString()));
Response.Write(string.Format("Message: {0}<br/>", ex.Message));
Response.Write(string.Format("Source: {0}<br/>", ex.Source));
}
}
private void TryDatabaseConnection()
{
string connString = ConfigurationManager.ConnectionStrings["ASPNETDBConnectionString"].ToString();
SqlConnection connection = new SqlConnection(connString);
try
{
connection.Open();
Response.Write("Success on Database Connection<br/>");
}
catch (Exception ex)
{
Response.Write("Failed on Database Connection<br/>");
}
finally
{
connection.Dispose();
}
}
}
}
I was able to connect to the database, but not the Membership bits. Rather than go through all of the steps, I will tell you what you need to do get this running quickly.
First, create your ASPNET Membership database. The easiest way to do this:
If you like to be a maverick, the install scripts are also located at %windir%/Microsoft .Net/Framework/v2.0.50727. It does not really matter how you create the database.
You now need to go to the sample MVC Membership web application and edit the web.config. By default, it is implicit and only contains the following Membership bits.
<connectionStrings>
<add name="ASPNETDBConnectionString"
connectionString="Data Source=.SQLEXPRESS;AttachDbFilename=|DataDirectory|ASPNETDB.MDF;Integrated Security=True;User Instance=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
…
<roleManager enabled="true" />
You need to fill this in. First, replace the connection string with the connection string to your database:
<connectionStrings>
<add name="ASPNETDBConnectionString"
connectionString="Server=(local);Database=ASPNETDB;UID={Membership User};PWD={user password};"
providerName="System.Data.SqlClient" />
</connectionStrings>
You then need to fill in the implied bits with real bits. This means the <roleManager enabled="true" /> will become something like:
<membership userIsOnlineTimeWindow="20" defaultProvider="AspNetSqlProvider">
<providers>
<add connectionStringName="ASPNETDBConnectionString"
minRequiredPasswordLength="8"
minRequiredNonalphanumericCharacters="1"
requiresQuestionAndAnswer="true"
applicationName="/"
passwordFormat="Encrypted"
passwordAttemptWindow="10"
enablePasswordReset="true"
enablePasswordRetrieval="true"
name="AspNetSqlProvider"
type="System.Web.Security.SqlMembershipProvider" />
</providers>
</membership>
<roleManager defaultProvider="RoleManagerProvider"
enabled="true"
cacheRolesInCookie="true"
cookieName=".ASPROLES"
cookieTimeout="30"
cookiePath="/"
cookieRequireSSL="false"
cookieSlidingExpiration="true"
cookieProtection="All">
<providers>
<add name="RoleManagerProvider"
type="System.Web.Security.SqlRoleProvider"
connectionStringName=" ASPNETDBConnectionString "
applicationName="/" />
</providers>
</roleManager>
Notice the highlighted section. It is important. The normal value here is hashed, but I want to be able to reverse engineer the passwords for admin purposes (a feature I will have to add to the MVC bits). To use encrypted, I will have to work with the machineKey section of the config. This looks like this:
<machineKey validationKey="{Validation Key Here}"
decryptionKey="{Decryption Key Here}"
validation="3DES"/>
If you need to gen keys, you can use Ben Strackany’s key generator. You will need a Code Project account (free) to download the project:
http://www.codeproject.com/KB/aspnet/machineKey.aspx
If you do not have an account on Code Project, and don’t want one, Peter Bromberg has created a sample page that will create these keys, as long as you want to use SHA1 for validation (yes, you can alter this):
http://www.eggheadcafe.com/articles/20030514.asp
The final step is seeding the database. The easiest way is to open the web configuration tool. When it connects to the database, it will create a record in aspnet_applications for the / application. You will have to go through the wizards to create your administrator accounts.
I have also included a script for you to run, if you are not big on using the web configuration tool. If you use the script, you will have to set up the following machineKey section in your web.config, as the passwords are encrypted using these settings (NOTE the validation key is on two lines as spaces truncates things — you must fix that in your web.config when you copy and paste this):
<machineKey
validationKey=
"2AB9BB5084E7E8E34ECAA89E37B542E50D0C7E486D606804021812400A6E2EEDAF80F5E4
798B44EC71B0403F91D33B3208C170A34453B2A51A9EDA27839C6552"
decryptionKey="B2F58C16DEBB63B7CE25D5CD239CCDB0D71E160FE7FD65BC6CD1D0C98BAD29FA"
validation="SHA1" decryption="AES" />
Here is the script:
/***********************************************
* ASP.NET MVC Application Seeder
* ——————————————–
* User Name: Administrator
* Password: password!1
*
* Security Question:
* What is the velocity of an unladen sparrow?
*
* Security Question Answer:
* European or African?
***********************************************/
– Create the application
insert into aspnet_applications (ApplicationName
, LoweredApplicationName
, ApplicationId
, [Description])
values (‘/’
, ‘/’
, ’3A168FD5-07E8-49FB-9C95-F8D15A9DA6E7′
, ‘Default application for the sample ASP.NET MVC Applicatio’)
GO
– Create the administrator role
insert into aspnet_roles (ApplicationId
, RoleId
, RoleName
, LoweredRoleName
, [Description])
values (’3A168FD5-07E8-49FB-9C95-F8D15A9DA6E7′
, ’072EA006-37E8-42DE-8FCA-BE790BF61BFA’
, ‘Administrator’
, ‘administrator’
, ‘Administrator role for the sample ASP.NET MVC Applicatio’)
GO
– Create the Administrator user
insert into aspnet_users (ApplicationId
, UserId
, UserName
, LoweredUserName
, IsAnonymous
, LastActivityDate)
values (’3A168FD5-07E8-49FB-9C95-F8D15A9DA6E7′
, ‘DE313B04-D60B-4E6B-B790-8F56FDB96C17′
, ‘Administrator’
, ‘administrator’
, 0
, GetUtcDate())
GO
– Create the user membership bits
insert into aspnet_membership(ApplicationId
, UserId
, [Password]
, PasswordFormat
, PasswordSalt
, MobilePIN
, Email
, LoweredEmail
, PasswordQuestion
, PasswordAnswer
, IsApproved
, IsLockedOut
, CreateDate
, LastLoginDate
, LastPasswordChangedDate
, LastLockoutDate
, FailedPasswordAttemptCount
, FailedPasswordAttemptWindowStart
, FailedPasswordAnswerAttemptCount
, FailedPasswordAnswerAttemptWindowStart
, Comment)
VALUES (’3A168FD5-07E8-49FB-9C95-F8D15A9DA6E7′
, ‘DE313B04-D60B-4E6B-B790-8F56FDB96C17′
, ‘e4esufQBWoB7uWAAaCMH5TgeqPmozkIp+YAYDrq9NInu+1ovT+GWfuDjLZPMMAiT’
, 2, ‘EzaAKgyIzgckUbbEzmW0lQ==’
, NULL
, ‘admin@company.com’
, ‘admin@company.com’
, ‘What is the velocity of an unladen sparrow?’
, ‘e4esufQBWoB7uWAAaCMH5W3g/LF9xTjDpDr0yA88cwd53dNpxV8JGkzVZPQV0a+cxrJ1v+osHSg7RLqTRU4Vaw==’
, 1
, 0
, GetUtcDate()
, CAST(0x00009AB8015CEC03 AS DateTime)
, CAST(0x00009AB8015CEBD4 AS DateTime)
, CAST(0xFFFF2FB300000000 AS DateTime)
, 0
, CAST(0xFFFF2FB300000000 AS DateTime)
, 0
, CAST(0xFFFF2FB300000000 AS DateTime)
, ‘Administrator for the sample ASP.NET MVC Application’)
GO
– Link the Administrator user to the Administrator role
insert into aspnet_usersinroles (UserId
, RoleId)
values (‘DE313B04-D60B-4E6B-B790-8F56FDB96C17′
, ’072EA006-37E8-42DE-8FCA-BE790BF61BFA’)
GO
You should now be able to log in with the admin credentials (listed in the flower box at the top of the SQL script).
Beyond learning how to hook the ASP.NET MVC site into SQL Server Standard, Dev or Enterprise, you have learned how to fix membership bits. Just follow the steps in this blog post to fix an errant membership database. Here is the order of troubleshooting:
Hope this helps!
Peace and Grace,
Greg
June 10, 2008 Leave a comment
Here is the basic step by step:
NOTE: I love the exception message here! ![]()
This part becomes important soon, but I have included it primarily for the error message.
public IQueryable<Category> GetCategories()
for (int i = 1; i <= 2; i++)
//Create children objects (hierarchy)
return result.AsQueryable<Category>();
Now, if you wanted to test your TestRepository, you would test this method first, but the important thing here is we are making sure our test class is solid before we go to testing the code that will use a repository (our service). We are also testing the test in this case.
IList<Category> categories = catalogService.GetCategories();
I don’t want to transcribe the entire video, so I will quit here, but you should now see that by passing in the object that gets the data, you can make a test object quite easily, without employing depency injection. This pattern is not applicable everywhere, of course, but it works here. You should also note that testing involves methodically going from test to code. Rob’s example might make a purist a bit peeved, but I am fine with working with stubs for your red light condition.
When I said "upside down" earlier, I was speaking of passing the Repository to the service so the service can be tested with more or less "arbitrary" test code. It is also a paradigm shift when one thinks about what is being tested versus where we usually being our code. In so many applications, we start writing the database first or the UI first and we work from end to end. In Rob’s example, you are working on the business logic first and moving outward to the UI and database. There are two benefits to this method:
To summarize, there are quite a few ways to avoid going to a database in a unit test. Two mentioned here are mocking the data in a mock object and flipping the pattern so your service takes an object as part of its constructor. I will have to save mocks for another day, as my verbal diarrhea has eaten up all of the time I have to write this post.
Peace and Grace,
Greg
June 9, 2008 3 Comments
* NOTE: I realize there are other choices, like hardcoding your drop down, etc., but I am trying to choose the saner options. ![]()
Both are perfectly good options, in certain situations. In general, however, you want the option that has the easiest path for change. This is not the enum. Now, there are instances where the enum option is going to be easier. In those situations, you probably want to put the enum in a library separate from your working bits, especially if you have tightly coupled your objects to your back end. I am going on tangents now that I have already discussed. Click here for a bit about separation of concerns.
Here is the situation I am looking at. First, the enum:
public
enum Foo : int
{
[Description("Bar Option 1")]
Bar = 1,
[Description("Bar Option 2")]
Bar2 = 20
}
Nice use of attributes to add descriptions, right? And, you can code something like so to pull the descirptions for your DropDownList:
ddEvent.Items.Insert(0,
new ListItem(enum_support.GetDescription(Foo.Bar), Convert.ToString((int)Foo.Bar)));
ddEvent.Items.Insert(0,
new ListItem(enum_support.GetDescription(Foo.Bar2), Convert.ToString((int)Foo.Bar2)));
At this time, it seems we are rather clever. But the boss asks to update the drop down to say change "Bar Option 1" to read "Power Up" and "Bar Option 2" to read "Power Down". You are now recompiling libraries for a simple label change. Ouch!
There is another issue with storing these items in an enum in this fashion. When I want to produce a report, I have to invoke something like this to fill in the report.
lblEvent.Text = r.Event.ToString();
I can’t simply pull the data from the database. I could add a lookup table to match the enum, but I then risk having the table and the enum out of sync. To keep it in sync, I could either generate the enum on the fly (in memory only) or code generate the enum, but why should I have this extra step? It makes no sense.
There are good reasons to use enums. Enums are great, for example, when you have an input for a method that can only take a limited set of values. You use the enum to avoid having to check values on the int being passed in. But this type of enum is specific to the class or classes in the library. It is not a global enum.
I am a proponent of using a persistent store for metadata. I have nothing against an enum class coded inside some helper library, but if the enum is used globally it better be in a helper library rather than located in the first library that uses it. The word is REFACTOR. ALthough I am not against these types of enums, I still believe that most of your global enums should be lookup tables. And, if you are grabbing data with numerics that correspond to the enum, I would state it is mandatory that you create a lookup table rather than an enum. By mandatory, I do not mean the enum will not work, just that it is less flexible (extensible) and requires much more work to update (compile versus SQL change).
Peace and Grace,
Greg
June 7, 2008 9 Comments
I have seen a few posts lately where people are having a hard time getting a database up to their ISP. While there are tutorials on using SQL Express and an embedded database (in the App_Data folder), I have seen very few posts that deal with publishing to a database directly. I am going to cover the SQL Server Database Publishing Wizard.
Before going forward, you have to have SQL Server installed locally. This can be SQL Server Express. You should also have Visual Web Developer installed, although I do not think this is mandatory, as the tutorial will show.
First, let’s take the direct route. It is the easiest way to get the database up. The less direct route will use most of the same steps, although it is a bit more complex, as you have to run the script on the server.
First, download the SQL Server Database Publishing Wizard 1.1 from Microsoft. The page can be found here. It is a fairly simple install, so I won’t go through its steps. Once you are installed, we can go on.
1. Start the Database Publishing Wizard – In Vista, you can type in the search box (just above the start button) to quickly get to it. If you have already upgrade from Vista to Windows XP ;-P, you can go to Program Files >> Microsoft SQL Server Database Publishing Wizard >> Database Publishing Wizard and start it there.
2. Click Next on the Welcome Screen
3. Select your local database to log into. For most of you, you will only have to change localhost (the default) to .SQLEXPRESS, as shown below:
NOTE: Since this is a web site, you can copy and paste your connection string for ASPNETDB into the bottom box and get in that way.
4. Click next and select a database. If you have provided a connection string, one will be pre-selected for you.
5. Click next.
Now, here is where paths may diverge a bit. If you have the ability to log onto your hosting database server via a web service, you can do it rather simple. We are going to show that way first.
6. Click on Publish to Shared Hosting Provider (where the arrow is pointing) and then click the More button.
7. Click the New Button (circled above) and then fill in the form.
8. In this instance, I have no databases, so I going to click New and create one.
9. Click OK, pick the database you created and then click OK. Then click finish and publish your database.
You have finished the first five steps and find you have no web service. Here is the longer way. This time, leave the Select an Output Location as Script to file and click finish.
There are three things to check here.
If you follow the instructions above, you should only be concerned with the middle one, as you need to find the file. Now, click finish again. When it is complete, you should see a screen like this:
If you have a script, you need to publish it. You will need the server name and credentials your ISP gave you to log into the database server. You will also need a tool to do it. Since you probably have SQL Server Express 2005, you should also have the SQL Server Management Studio Express. If not, you can download from here. I will use SQL Server Management Studio (full version, not express); the only difference is my screens may look a bit different than yours. Here are the steps:
1. Open the Management Studio
2. Alter the Server name and user id and password to log into your server. The pieces you will have to change are circled below. You will also have to change authentication to SQL Server Authentication to enter user id and password.
3. Select your database in the Object Explorer (upper left side of the tool) or click View >> Object Explorer if it is not showing. With an ISP, you will see a long list generally (unless they have filtered permissions properly), so select yours.
4. Click on the New Query Button
5. Click on the Open File button and find your file (alternatively you can choose either File >> Open >>. File or the keystroke Control + O).
6. Click the Execute button (or press F5)
Your database is now deployed to the server.
If you developed locally, you will have the wrong connection string. Let’s fix that.
1. Open your web development tool (this can be Visual Studio, Visual Web Developer Express, Expression Web, Dreamweaver, etc.).
2. Open the web.config file
3. Find the <membership> section so you know which connection string to change (bold below):
<membership>
<providers>
<add
name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider, …"
connectionStringName="ASPNETDB"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="true"
applicationName="/"
requiresUniqueEmail="false"
passwordFormat="Hashed"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="7"
minRequiredNonalphanumericCharacters="1"
passwordAttemptWindow="10"
passwordStrengthRegularExpression=""
/>
</providers>
</membership>
4. Go the <connectionStrings> section and find that connection string. In this case, we are looking for ASPNETDB.
<connectionStrings>
<add name="ASPNETDB" value="Data Source=.SQLEXPRESS;AttachDbFilename="C: somewebsiteApp_DataASPNETDB.MDF";Integrated Security=True;Connect Timeout=30;User Instance=True"/>
</connectionStrings>
5. Change the connection string to the one for your host
<connectionStrings>
<add name="ASPNETDB" value="server={server name};database={your database name:;UID={user id};pwd={password};"/>
</connectionStrings>
You will obviously have to change {server name} to the actual server, like p3swhsql-v15.shr.phx3.secureserver.net for one of Go Daddy’s servers. But, once that is done, you should be rocking.
Hope this helps …
Peace and Grace,
Greg
Twitter: @gbworld