tag:blogger.com,1999:blog-31513810167448552532024-03-05T08:24:04.146-08:00The Dewdum BeeA developer's buzz on working with Azure, Visual Studio, SQL Server, and yes... DW, DM & BIKrishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.comBlogger25125tag:blogger.com,1999:blog-3151381016744855253.post-64654342054617198052021-10-31T06:24:00.000-07:002021-10-31T06:24:33.307-07:00Create Identity in a new Schema in Visual Studio 2022 and .NET 6 / ASP.NET MVC 6<p>Identity tables are created in the [dbo] schema by default, when using SQL Server. We can create them in a different schema if we want to, in a few simple steps.</p><p>(This does not migrate existing data, but rather, it creates new tables in the specified schema.)</p><h4 style="text-align: left;">The six steps required are given below.</h4><div><p style="text-align: left;"><span style="background-color: #fce5cd;"><span style="color: #cc0000;"><b><br /></b></span></span></p><p style="text-align: left;"><span style="background-color: #fce5cd;"><span style="color: #cc0000;"><b>BEFORE STARTING ... MOST IMPORTANT:</b></span></span></p><p style="text-align: left;"><span style="background-color: #fce5cd;"><span style="color: #cc0000;">Make a complete backup of your database before doing this! You must ensure you can recover everything from a backup if things don't work out as expected. </span></span></p><div><br /></div></div><p style="text-align: left;"><b>1. Set the connection string in </b><span style="font-family: Inconsolata;">appsettings.json</span><b>. For example:</b></p><div style="text-align: left;"><span style="font-family: Inconsolata;"><span>"ConnectionStrings": {<br /></span><span> "DefaultConnection": "Server=<span style="background-color: #fcff01;">YOUR-SERVERNAME</span>;Database=<span style="background-color: #fcff01;">YOUR-DBNAME</span>;Trusted_Connection=True;MultipleActiveResultSets=true"<br /></span><span> }</span></span></div><p>You can specify a local or network server, or it could be an Azure SQL database.</p><p><br /></p><p style="text-align: left;"><b>2. Under the </b><span style="font-family: Inconsolata;">Properties </span><b>folder in your web project:</b></p><p>Open the files <span style="font-family: Inconsolata;">serviceDependencies.json</span> and <span style="font-family: Inconsolata;">serviceDependencies.local.json</span>, and comment out the code in both of them:</p><div style="text-align: left;"><span style="font-family: Inconsolata;">//{<br />// "dependencies": {<br />// "mssql1": {<br />// "type": "mssql",<br />// "connectionId": "ConnectionStrings:DefaultConnection"<br />// }<br />// }<br />//}</span></div><p style="text-align: left;"><b><br /></b></p><p style="text-align: left;"><b>3. In </b><span style="font-family: Inconsolata;">Program.cs</span><b>, create 2 new classes </b><span style="font-family: Inconsolata;"><span>ApplicationUser</span><span> </span></span><b>and </b><span style="font-family: Inconsolata;">ApplicationDbContext</span><span style="font-family: courier; font-weight: bold;">,</span><b> placing them at the end of the file:</b></p><p style="text-align: left;">(The new classes derive from <span style="font-family: Inconsolata;">IdentityUser </span>and <span style="font-family: Inconsolata;">IdentityDbContext</span>, respectively.)</p><div style="text-align: left;"><span style="font-family: Inconsolata;"><span>#region App-specific code added for placing identity tables into a new schema</span><span><br /></span><span>// https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-6.0</span><span><br /></span><span>/// <summary><br /></span><span>/// Extra fields can be added to the user table here<br /></span><span>/// </summary><br /></span><span>public class <span style="background-color: #01ffff;">ApplicationUser </span>: IdentityUser<br /></span><span>{<br /></span><span> //public string CustomTag { get; set; }<br /></span><span>}</span><span><br /></span><span><br /></span><span>public class <span style="background-color: #01ffff;">ApplicationDbContext </span>: IdentityDbContext<ApplicationUser><br /></span><span>{<br /></span><span> public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)<br /></span><span> : base(options)<br /></span><span> {<br /></span><span> }</span><span><br /></span><span><br /></span><span> /// <summary><br /></span><span> /// Create ident tables in a different schema<br /></span><span> /// </summary><br /></span><span> protected override void OnModelCreating(ModelBuilder builder)<br /></span><span> {<br /></span><span> base.OnModelCreating(builder);</span><span><br /></span><span> builder.HasDefaultSchema("<span style="background-color: #fcff01;">YOURCUSTOMSCHEMA</span>");<br /></span><span> }<br /></span><span>}<br /></span><span>#endregion</span></span></div><p style="text-align: left;"><span style="background-color: #fcff01;">Your new schema will be whatever you specify as <span style="font-family: Inconsolata;"><span>YOURCUSTOMSCHEMA</span> </span>at the end of the code above.</span></p><p style="text-align: left;"><b><br /></b></p><p style="text-align: left;"><b>4. Also in</b><span style="font-family: Inconsolata;"> Program.cs</span><b>, change references from </b><span style="font-family: Inconsolata;">IdentityUser </span><b>and </b><span style="font-family: Inconsolata;">IdentityDbContext</span> <b>to </b><span style="font-family: Inconsolata;"><span>ApplicationUser</span><span> </span></span><b>and </b><span style="font-family: Inconsolata;">ApplicationDbContext </span><span style="font-family: Inconsolata; font-weight: bold;">respectively - for example:</span></p><div style="text-align: left;"><span style="font-family: Inconsolata;">builder.Services.AddDbContext<<span style="background-color: #01ffff;">ApplicationDbContext</span>>(options =><br /></span><span style="font-family: Inconsolata;"> options.UseSqlServer(connectionString));<br /></span><span style="font-family: Inconsolata;">builder.Services.AddDatabaseDeveloperPageExceptionFilter();</span><span style="font-family: Inconsolata;"><br /></span><span style="font-family: Inconsolata;">builder.Services.AddDefaultIdentity<<span style="background-color: #01ffff;">ApplicationUser</span>>(options => options.SignIn.RequireConfirmedAccount = true)<br /></span><span style="font-family: Inconsolata;"> .AddEntityFrameworkStores<<span style="background-color: #01ffff;">ApplicationDbContext</span>>();</span></div><p style="text-align: left;"><span style="font-family: Inconsolata;"></span></p><p style="text-align: left;"><b><br /></b></p><p style="text-align: left;"><b>5. In </b><span style="font-family: Inconsolata;">Views\Shared\_LoginPartial.cshtml</span><b>, change references from </b><span style="font-family: Inconsolata;">IdentityUser </span><b>to </b><span style="font-family: Inconsolata;">ApplicationUser</span><b>:</b></p><div style="text-align: left;"><div><span style="font-family: Inconsolata;">@inject SignInManager<<span style="background-color: #01ffff;">ApplicationUser</span>> SignInManager</span></div><div><span style="font-family: Inconsolata;">@inject UserManager<<span style="background-color: #01ffff;">ApplicationUser</span>> UserManager</span></div><p style="text-align: left;"><b><br /></b></p><p style="text-align: left;"><b>6. Create and run migrations, to generate the new schema and identity tables:</b></p><div style="text-align: left;">- Click on <span style="font-family: Inconsolata;">Tools > NuGet Package Manager > Package Manager Console</span> (PMC), to get a PMC window</div><div style="text-align: left;"><br /></div><div style="text-align: left;">- Create the migration by entering the following command against the PM prompt:</div><div style="text-align: left;"><span style="font-family: Inconsolata;">PM> add-migration <span style="background-color: #fcff01;">IdentCreation </span>-context ApplicationDbContext</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="background-color: #fcff01;">You can specify any name for the migration, <span style="font-family: Inconsolata;">IdentCreation</span> in the above example.</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">The migration file will be generated, and automatically opened, by Visual Studio 2022.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="background-color: #fce5cd;"><span style="color: #cc0000;"><b>IMPORTANT!</b> Check the resulting code in it to make sure all looks good.</span></span></div><div style="text-align: left;"><span style="background-color: #fce5cd;"><span style="color: #cc0000;"><br /></span></span></div><div style="text-align: left;"><span style="background-color: #fce5cd;"><span style="color: #cc0000;"><b>IMPORTANT!</b> Up till now, nothing has changed in your database. The next command updates the database!</span></span></div><div style="text-align: left;"><span style="background-color: #fce5cd;"><span style="color: #cc0000;"><br /></span></span></div><div style="text-align: left;"><div><span style="background-color: #fce5cd;"><span style="color: #cc0000;"><b>AND ... MOST IMPORTANT!</b> Make a complete backup of your database before running the next command!</span></span></div><div><br /></div></div><div style="text-align: left;">- Run the migration to update your database:</div><div style="text-align: left;"><span style="font-family: Inconsolata;">PM> update-database -context ApplicationDbContext</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">This will create the new identity tables in your database, and will also create the schema itself if it doesn't already exist.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>That's it - done! On running the program, when you add a new user, they'll be added into the tables in the new schema.</b></div><div style="text-align: left;"><br /></div></div>Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-81031874086330240252014-11-12T10:44:00.000-08:002014-11-12T10:44:22.488-08:00Visual Studio Community 2013 - FREE!Microsoft announces Visual Studio Community Edition 2013 -- for free!!<div>
<br /></div>
<div>
Amazing!</div>
<div>
<br /></div>
<div>
<a href="http://www.visualstudio.com/products/visual-studio-community-vs">http://www.visualstudio.com/products/visual-studio-community-vs</a></div>
<div>
<br /></div>
<div>
And not just that - use it on a Mac, or on Linux :-)</div>
<div>
<br /></div>
<div>
Extract from the Q&A on the above page:</div>
<div>
<br /></div>
<div>
<div style="background-color: #eeeeee; color: #333333; font-family: 'Segoe UI', Tahoma, Helvetica, sans-serif; font-size: 13px; line-height: 20px; padding-bottom: 0px; padding-right: 200px;">
<div style="padding-bottom: 0px; padding-right: 200px;">
<strong>Q: Who can use Visual Studio Community?</strong><br />A: Here’s how individual developers can use Visual Studio Community:</div>
<ul style="margin: 0px 0px 10px 35px; padding: 0px;">
<li style="margin: 0px 0px 2px 15px; padding: 0px;">Any individual developer can use Visual Studio Community to create their own free or paid apps.</li>
</ul>
<div style="padding-bottom: 0px; padding-right: 200px;">
Here’s how Visual Studio Community can be used in organizations:</div>
<ul style="margin: 0px 0px 10px 35px; padding: 0px;">
<li style="margin: 0px 0px 2px 15px; padding: 0px;">An unlimited number of users within an organization can use Visual Studio Community for the following scenarios: in a classroom learning environment, for academic research, or for contributing to open source projects.</li>
<li style="margin: 0px 0px 2px 15px; padding: 0px;">For all other usage scenarios: In non-enterprise organizations, up to 5 users can use Visual Studio Community. In enterprise organizations (meaning those with >250 PCs or > $1MM in annual revenue), no use is permitted beyond the open source, academic research, and classroom learning environment scenarios described above.</li>
</ul>
</div>
<div style="background-color: #eeeeee; color: #333333; font-family: 'Segoe UI', Tahoma, Helvetica, sans-serif; font-size: 13px; line-height: 20px; padding-bottom: 0px; padding-right: 200px;">
<div style="margin-bottom: 10px; padding: 0px 0px 10px;">
<strong>Q: How does Visual Studio Community 2013 compare to other Visual Studio editions?</strong><br />A: Visual Studio Community 2013 includes all the great functionality of Visual Studio Professional 2013, designed and optimized for individual developers, students, open source contributors, and small teams.</div>
<div style="margin-bottom: 10px; padding: 0px 0px 10px;">
<br /></div>
</div>
</div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com2tag:blogger.com,1999:blog-3151381016744855253.post-82812345326673165402014-08-30T10:11:00.000-07:002014-08-30T10:13:21.608-07:00Using integer PK instead of GUID, in ASP.NET Identity 2.1For a new project, I wanted to use auto-incrementing Int keys for the userid (instead of the default GUID). Identity 2.x supports this change, and there are lots of guidelines online for the steps. The steps went mostly smoothly all the way, as far as the program modifications were concerned.<br />
<br />
From the database aspect, though, some points to note are:<br />
<br />
1. A "database migration" has to be done, to enable the system to build the tables correctly, with INT columns instead of nvarchar(128) for the Id column in various tables.<br />
<br />
2. The Id columns in the tables DO NOT automatically get created as IDENTITY types - they get created as simple Int columns (even though the "Up" migration states "identity: true" for them). So it is essential to do these steps:<br />
<br />
a. Run the program and try to create a new user - this will create the tables in the database, but will come up with a big error page, stating: Cannot insert the row because the Id column does not allow nulls.<br />
<br />
b. Go to the database, and modify the Id column to set it to an Identity type, for the following tables: AspNetRoles and AspNetUsers.<br />
<br />
Then run the program again, create a new user - everything should work as expected now.<br />
<br />Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-26641666829427382112014-06-17T22:43:00.001-07:002014-06-18T14:03:35.753-07:00Some Android tips for long-time iPhone usersLast week, I took the plunge and switched from iPhone to an Android phone. After 4 years of being an iOS user, it was not easy!<br />
<br />
My new phone is a <a href="http://www.amazon.com/gp/product/B00K0NRZSW/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00K0NRZSW&linkCode=as2&tag=fitloop-20&linkId=ROS4OIO4XHASKZVJ">Motorola MOTO-G 4G LTE</a>. At $220, unlocked and including LTE, I think it's a great deal. Based upon my experiences as I learn about Android and this phone, I'll post tips here which I hope you'll find useful. Many of them are things done just differently, and many of them are hard (or even impossible) to achieve on an iPhone.<br />
<br />
(I'll update this page with more tips progressively ... please visit regularly!)<br />
<br />
<h4>
1. Putting the phone in "Silent Mode":</h4>
Just press and hold the power button. A window will come up with a set of options. The bottom row has icons for Silent / Vibrate / Normal. Just tap the option you want.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7axsPnIBQhc6RxHEYjQ_dZ5UHqy43yTudMmwgFDSD1WzS_G9XFN-myCC82o4k9QTy3l4uCarsZAceHA9WE6dBjqg2THG2KNBDbJxaBRTuK-2lb6oTwQQOdvz4z0Zx075133XtufDXkow/s1600/Android-power-and-sound-options-screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7axsPnIBQhc6RxHEYjQ_dZ5UHqy43yTudMmwgFDSD1WzS_G9XFN-myCC82o4k9QTy3l4uCarsZAceHA9WE6dBjqg2THG2KNBDbJxaBRTuK-2lb6oTwQQOdvz4z0Zx075133XtufDXkow/s1600/Android-power-and-sound-options-screenshot.png" height="320" width="180" /></a></div>
<br />
Added bonus: Power Off or Airplane Mode can be selected from right there, too.<br />
<br />
TIP: No need to move the slider to get to the Home Screen first. Press the power button once so the screen lights up, then press again and hold to get the options.<br />
<br />
<h4>
2. Night mode: Automatically put on silent sleep mode each night</h4>
Bring up the Assist app (Home screen > App launcher (circle with 6 dots) > Assist icon). Configure the "Sleeping" option to your preferences. The phone will automatically go into silent mode during those hours.<br />
<br />
<b>But don't miss urgent calls!</b> Tap the right-arrow next to the "Silence" checkbox, to get a couple of useful/essential options: Enable the phone to ring when a person from your Favorites list calls, or when someone calls twice within 5 mins.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtWaKGzwHU1FS9xetHGDYctXvwcmR_om-25mrp4-Cuil32mAjBBL0aciejxHXiHr_xE1IuaR9A9pu24VTfVfX_IgNOgIioTgjM8nfq4Sok5jsY5qX4Z6NYR9Si3pQT9qascvY2L6497jI/s1600/Android-night-sleep-mode-screenshot.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtWaKGzwHU1FS9xetHGDYctXvwcmR_om-25mrp4-Cuil32mAjBBL0aciejxHXiHr_xE1IuaR9A9pu24VTfVfX_IgNOgIioTgjM8nfq4Sok5jsY5qX4Z6NYR9Si3pQT9qascvY2L6497jI/s1600/Android-night-sleep-mode-screenshot.png" height="320" width="180" /></a></div>
<br />
TIP: Along with "Sleeping", another useful option there is "Meeting". It uses your calendar to put the phone in silent mode when you're in meetings. Check it out!<br />
<br />
<br />Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-52295200412960785342013-09-25T17:20:00.001-07:002013-09-25T17:20:14.728-07:00Exporting a local SQL Server table and its data to SQL AzureFor a recent project, I needed to export a table from my local SQL Server to a SQL Azure implementation.<br />
<br />
<h3>
Exporting an entire database</h3>
Now, if it is required to export the entire database (as opposed to a single table), it is an easy matter using SQL Server Management Studio (SSMS): Right-click on the DB name -> Tasks -> Deploy Database to SQL Azure.<br />
<br />
But the problem with trying to export just a single table (and all of its data) into SQL Azure is that the latter does not permit statements like INSERT INTO ... SELECT FROM, due to connection limitations across servers (or even across databases).<br />
<br />
Therefore, it is not even possible to do the 2-step process of first deploying your database from the local to the Azure server, and then inserting table data from the resulting Azure database to the desired one within Azure... because that too would involve a SELECT across databases.<br />
<br />
It is also not possible to create a BACPAC for just the desired table, and then import the table from that BACPAC into an existing Azure database. BACPAC data can be imported ("deployed") only into a new database in SQL Azure.<br />
<br />
<h3>
Exporting a single table: The robust solution</h3>
One possible approach is to write a simple client app (e.g. a C# project) that would read data from the local database and then push it to the remote SQL Azure db. This is a robust approach, but is not quick and easy if all you need is to quickly push a relatively small table (up to a few 10's of thousands of rows) to Azure.<br />
<br />
<h3>
The quick and easy solution for relatively small tables</h3>
Here's a solution that accomplishes the job simply and conveniently. It involves generating a script form within SSMS; the script contains CREATE TABLE and INSERT statements with the data.<br />
<br />
Here's how to create the script, which includes INSERTs for the data content:<br />
<br />
<ol>
<li>In SSMS, right-click on your local database, go to Tasks -> Generate Scripts</li>
<li>In the Choose Objects tab, click on Select specific database objects, and then expand the Tables treeview and select the table(s) you want to export</li>
<li>In the Set Scripting Options tab, click the Advanced button to get to the Options window, and in that window, change 'Types of data to script' to: Schema and data (default is Schema only).</li>
<li>(Take this opportunity to look through the other options such as whether you want the Indexes to be scripted as well - lots to learn here!)</li>
<li>Save the script to a file.</li>
</ol>
<div>
<br /></div>
<div>
And then run that script and get the data into SQL Azure:</div>
<br />
<br />
<ol>
<li>Click on your SQL Azure db name in SSMS (where you want to transfer the data), and then open the script file which you had saved in the previous set of steps. Make sure that the window tab is showing that you're connected to your Azure server and not to your local server.</li>
<li>Look through the top part of the script - there is likely to be a USE statement referring to your source database name - comment it out because it's not supported and we don't need it</li>
<li>Make sure that the table being created has a CLUSTERED PRIMARY KEY. If not, add it (adding an IDENTITY column, if it does not exist). This is important - Azure will not accept data into the table if it doesn't have a clustered PK.</li>
<li>Double-check that the connection for this window is correct: SQL Azure server, correct target database.</li>
<li>Run the script.</li>
</ol>
<div>
In my test, it inserted almost 1,000 rows of data per minute. Total data was about 40K rows, so it took nearly 45 mins to transfer. Also, the connection was lost once, about 15 minutes into the process, so I had to remove that bunch of (nearly 15K) INSERT statements and then run the query again.</div>
<div>
<br /></div>
<div>
In the end, this process saved a lot of time in writing client-side code to read local and write remote data, and was very much worth it for the purpose.</div>
<div>
<br /></div>
<div>
Happy coding!</div>
<div>
<br /></div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-90704569439139898342013-08-08T10:41:00.000-07:002013-08-08T10:41:23.041-07:00A 3-D approach to Software Architecture<h3>
<span style="font-family: Trebuchet MS, sans-serif;">The 3 'D's of software architecture: Discuss - Design - Develop</span></h3>
<span style="font-family: Trebuchet MS, sans-serif;">The role of a good software architect begins very early, at the requirements understanding stage, and continues throughout the application lifecycle all the way to delivery and support. Such an architect's role can be summarized as: Understanding the client's business problems, evaluating them, and then helping to deliver optimum solutions that meet the clients' needs well, and at the same time are clean and maintainable.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">To achieve it: DISCUSS, DESIGN, and DEVELOP. A good rule of thumb: Spend 1/3 of your total time on each of these.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><b>DISCUSS.</b> An architect will start the project conceptualization by discussing with the customer to find out their exact problems and their desired solutions. The "customer" may be external (a business client) or internal (another department within the company), or may even be a Business Analyst assigned to work with the end-user.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;">Focusing on finding out the real problems, separating the must-haves from the good-to-haves, and then prioritizing the ones that are most important for the customer's business, is crucial. And there's no way it can be achieved without extensive and intensive discussions.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><b>DESIGN</b>. This is the phase that connects the customer's business requirements with the technical software solution. Consider factors and requirements such as: Scalability - what volumes of data / updates / traffic / registrations are we talking about? Responsiveness - what's an acceptable response time... 1 second or 1 minute for a query? Platforms and toolsets, security, availability, disaster recovery - all these, and more, factor into a rock-solid design.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><b>DEVELOP</b>. Finally, work closely with the development team to make sure everything is implemented properly. There's just no way to simply draw a bunch of UML diagrams, hand them over to the dev team, and walk on over to the next project. Effective architects will work hand-in-hand with the developers, because often design portions change, unexpected problems come up - even customers' own requirements evolve (really!).</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><b>And, of course, the 4th "D": DELIVER!</b> Delivery of a solution that meets the customer's needs, on-time, under-budget, and is ready not only for easy maintenance but also for future extensibility, is the goal of a software architect's work.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><b>And "delivery" doesn't mean just handing over the solution, and then moving on!</b> Code-complete is just the beginning. Architects take responsibility for the entire application lifecycle (ALM) of their projects: They will continue to give high-level technical assistance to their customers, research and recommend relevant upgrades to management and customers, coordinate with the development, QA and support teams to make sure customers' needs remain addressed, and keep tweaking their system architecture to keep it updated and in pace with relevant technology advancements.</span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<span style="font-family: Trebuchet MS, sans-serif;"><i>What do you think? As a software architect, do you regularly find yourself using this philosophy in your day-to-day work? Please do share your thoughts and comments!</i></span><br />
<span style="font-family: Trebuchet MS, sans-serif;"><br /></span>
<br />Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-34123072338596525102013-07-21T12:13:00.000-07:002013-08-08T09:42:10.214-07:00Programatically accessing your custom fields in VS2012 SimpleMembership<i>(This is the 3rd of a 3-article series, on using SimpleMembership along with customized fields)</i><br />
<i><br /></i>
<i>Article 1: <a href="http://www.dwdmbi.com/2012/09/visual-studio-2012-uses.html">Visual Studio 2012 uses SimpleMembership as its new Membership Provider</a></i><br />
<i>Article 2: <a href="http://www.dwdmbi.com/2012/10/adding-custom-fields-to-vs2012-mvc4.html">Adding custom fields to a VS2012 MVC4 SimpleMembership system</a></i><br />
<i>Article 3: This article.</i><br />
<br />
In the previous article, I covered how to create custom Membership fields, such as a registered user's name or phone number. This article explains how to use these added fields programatically from within your project.<br />
<br />
The key steps that we'll follow can be summarized as:<br />
<br />
<ol>
<li>Define the additional fields in the UserProfile class, so we can access them programatically (and get IntelliSense for them as well);</li>
<li>Add them to the RegisterModel class, and various other places per details in the above article, so user's can supply values into them when registering;</li>
<li>Run the program and go to the "Register" page (but do not create a new user yet. This will create the database tables (if they're not present);</li>
<li>Modify the UserProfile table and add the required fields, such as FirstName and LastName;</li>
<li>Tweak some code in the system-generated InitializeSimpleMembershipAttribute.cs program, to avoid an exception when the system makes a duplicate call to InitializeDatabaseConnection();</li>
<li>Add code to the Index action method in the Home controller, to actually use the new fields we have added.</li>
<li>Run the program, create a new user and supply data into the new fields, and test out everything.</li>
</ol>
<div>
Out of the above, steps 1 thru 4 are already covered in the article previously referenced. Please follow all the steps in that article, to make sure we've got the base correctly set up! So let us start from step-5.</div>
<div>
<br /></div>
<div>
NOTE: The above article only defined a single additional column - FullName. But here, we're using 2 additional columns - FirstName and LastName (in order to illustrate the use of multiple columns). In your own code you can, of course, create whatever columns you need to use, just making sure that they're consistent across the board.</div>
<div>
<br /></div>
<div>
(HEADS-UP: I have called my test project "SimpleMembershipCustomFields", so you'll see that namespace reference in the code given below. You'll need to replace that with your own project namespace reference.)</div>
<div>
<br /></div>
<h4>
Tweak some code in the system-generated InitializeSimpleMembershipAttribute.cs program</h4>
<div>
<div>
The Websecurity method call already exists in the program. Enclose that code in an if() condition, else it sometimes gets called multiple times when doing various Membership-related tasks resulting an exception:</div>
<div>
<br /></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> if (!WebSecurity.Initialized)</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> {</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> }</span></div>
</div>
<div>
<br /></div>
<h4>
Add code to the Index action method in the Home controller, to actually use the new fields</h4>
<div>
For demo purposes, I've simply added a text string derived from the newly added fields (FirstName and LastName).</div>
<div>
<br /></div>
<div>
First, in the "using" section, add the following namespace references:</div>
<div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;">// Customization: References to the additional field(s)</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;">using WebMatrix.WebData;</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;">using SimpleMembershipCustomFields.Models;</span></div>
</div>
<div>
(substituting your own project's namespace reference instead of "SimpleMembershipCustomFields")</div>
<div>
<br /></div>
<div>
Next, add code in the Index action method to use the new fields. For your ready reference, I've simply copied over the entire Index() method. The code I've added here is all inside the IsAuthenticated condition; the rest is just the default generated program:</div>
<div>
<br /></div>
<div>
<div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;">public ActionResult Index()</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;">{</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"><br /></span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> if (WebSecurity.IsAuthenticated)</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> {</span></div>
<div>
<span style="color: #6aa84f; font-family: Trebuchet MS, sans-serif;"> // The call to InitializeDatabaseConnection is mandatory before calling</span></div>
<div>
<span style="color: #6aa84f; font-family: Trebuchet MS, sans-serif;"> // any other WebSecurity method (test it by commenting it out)</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> if (!WebSecurity.Initialized)</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> {</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> WebSecurity.InitializeDatabaseConnection(</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> "DefaultConnection", "UserProfile", "UserId", "UserName", </span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> autoCreateTables: false);</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> }</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"><br /></span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> UsersContext uc = new UsersContext();</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> UserProfile userProfile = uc.UserProfiles.Where(x => x.UserId == WebSecurity.CurrentUserId).FirstOrDefault();</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"><br /></span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> ViewBag.Message = "Hello, " + userProfile.FirstName + </span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> "! Your last name is: " + userProfile.LastName + ". " +</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> "Welcome, " + userProfile.FirstName + " " + userProfile.LastName + "!";</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> }</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"><br /></span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;"> return View();</span></div>
<div>
<span style="color: blue; font-family: Trebuchet MS, sans-serif;">}</span></div>
</div>
</div>
<div>
<br /></div>
<div>
Something to note here is that we're (conditionally) calling the InitializeDatabaseConnection() method, although the last parameter is "false" (it was "true" in the call from InitializeSimpleMembershipAttribute.cs). If you're going to need this call from multiple places in your project, it may make sense to extract it out into its own separate method, with "autoCreateTables" as a param.</div>
<div>
<br /></div>
<h4>
Run the program, and test out everything.</h4>
<div>
If all is well, you should be able to create a new user and supply values into the new fields, using the Register screen. The resulting Index page should look similar to the screenshot below. I've also added screenshots for the signed-out homepage as well as the database table contents.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMoHF76JtpwC3QctxxPlEgG8t00wz7hr3bZbI6Q0gDPcWJTC2D06KtcprWVZ7fYe4I-E-Pa0m6YHb0DUf4B-TfjAEzbQcf0Sn0hyphenhyphenD0WLffOIjUopRoxXm3xB24ABZJbnZ1HB_nYTYfCds/s1600/SimpleMembership+additional+fields+-+signed-in+view.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="182" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMoHF76JtpwC3QctxxPlEgG8t00wz7hr3bZbI6Q0gDPcWJTC2D06KtcprWVZ7fYe4I-E-Pa0m6YHb0DUf4B-TfjAEzbQcf0Sn0hyphenhyphenD0WLffOIjUopRoxXm3xB24ABZJbnZ1HB_nYTYfCds/s320/SimpleMembership+additional+fields+-+signed-in+view.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrYBmQSDF3NO97DwGgRaXRzy-B6uJdgQKLljNkdYTeoK3vRPMO31tX3sEhO7kCmzlW2YubjNxMPN5t9Gvigo806uGRIwKQRIxP2PEJ3dykwan9PU8D4iqe1HKs6w-gqcNEVducKY2_88E/s1600/SimpleMembership+additional+fields+-+signed-out+view.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrYBmQSDF3NO97DwGgRaXRzy-B6uJdgQKLljNkdYTeoK3vRPMO31tX3sEhO7kCmzlW2YubjNxMPN5t9Gvigo806uGRIwKQRIxP2PEJ3dykwan9PU8D4iqe1HKs6w-gqcNEVducKY2_88E/s320/SimpleMembership+additional+fields+-+signed-out+view.PNG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEcsBkNAcF3OvL-uHjAr3R-NVxWzNc7EEgetfDoXWYwTdKf6a1La_fGNk8iUfOZ_6TTSiKZWCJ7hKmIDF-EY_M_CH2DL3rF0IYPXysJ1Uvt5QCJorWFL1QwvpZvCx7zI_HI5Lw8huL8z8/s1600/SimpleMembership+additional+fields+-+data+table+view.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="78" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEcsBkNAcF3OvL-uHjAr3R-NVxWzNc7EEgetfDoXWYwTdKf6a1La_fGNk8iUfOZ_6TTSiKZWCJ7hKmIDF-EY_M_CH2DL3rF0IYPXysJ1Uvt5QCJorWFL1QwvpZvCx7zI_HI5Lw8huL8z8/s320/SimpleMembership+additional+fields+-+data+table+view.PNG" width="320" /></a></div>
<br />
<div>
<br /></div>
<div>
Thanks for reading! Happy coding!</div>
<div>
<br />
<i>(This is the 3rd of a 3-article series, on using SimpleMembership along with customized fields)</i><br />
<i><br /></i>
<i>Article 1: <a href="http://www.dwdmbi.com/2012/09/visual-studio-2012-uses.html">Visual Studio 2012 uses SimpleMembership as its new Membership Provider</a></i><br />
<i>Article 2: <a href="http://www.dwdmbi.com/2012/10/adding-custom-fields-to-vs2012-mvc4.html">Adding custom fields to a VS2012 MVC4 SimpleMembership system</a></i><br />
<i>Article 3: This article.</i></div>
<div>
<i><br /></i></div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com2tag:blogger.com,1999:blog-3151381016744855253.post-64352054187740824922013-07-10T11:22:00.000-07:002013-07-21T17:02:25.207-07:00Avoid pausing Bootstrap Carousel on mouse hoverFor one of my projects, I'm using Twitter Bootstrap (ver 2.3.2) as the CSS framework, along with FontAwesome font-based icons. What a great toolset! Highly recommended.<br />
<br />
The Bootstrap "Carousel" JavaScript control is a very nice implementation. However, it has 2 default settings which I wanted to override and substitute with my own preferences:<br />
<br />
<ol>
<li>By default it pauses when the mouse is hovering over the control. This caused a problem because as soon as the user goes to the site, if by chance they move the mouse and bring it over the carousel, it stops cycling and leaves the user thinking that they need to click the buttons to make it move.</li>
<li>Also, without the mouse hovering over it, the carousel keeps auto-transitioning endlessly. This is quite needless and in fact counterproductive, as it becomes a distraction to the reader and stops them from focusing on the rest of the page content.</li>
</ol>
<br />
<br />
To solve these, I wanted to:<br />
<br />
<ol>
<li>Keep the carousel cycling even on mouse-over (users can always come back to a particular slide by clicking the navigation buttons in the carousel);</li>
<li>Stop auto-transitioning after the last slide has rendered.</li>
</ol>
<br />
Here's the JavaScript that needs to be added to the end of the page, in order to accomplish the tasks:<br />
<br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> <script type="text/javascript"></span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> </span><span style="color: #6aa84f; font-family: Courier New, Courier, monospace;">@*Start up the carousel, and override the default mouseover pause*@</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> $('.carousel').carousel({</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> interval: 5000,</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> pause: 'none'</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> })</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"><br /></span>
<span style="color: blue; font-family: Courier New, Courier, monospace;"> </span><span style="color: #6aa84f; font-family: Courier New, Courier, monospace;">@*Stop auto-transition after the last slide*@</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> $('#myCarousel').on('slid', '', function () {</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> if ($('.carousel-inner .item:last').hasClass('active')) {</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> $(this).carousel('pause');</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> })</span><br />
<span style="color: blue; font-family: Courier New, Courier, monospace;"> </script></span><br />
<div>
<br /></div>
<div>
The "<span style="background-color: #ffe599; color: blue; font-family: Courier New, Courier, monospace;">interval: 5000</span>" specifies that the slides should transition after 5 seconds, and also ensures that they actually in fact *do* start transitioning upon page load. Without that code, sometimes the transition doesn't start until the user clicks on the "next slide" navigation, which would be another irritant.</div>
<div>
<br /></div>
<div>
(The above code is using the MVC Razor syntax of having comments enclosed inside <span style="background-color: #ffe599; color: blue; font-family: Courier New, Courier, monospace;">@*comment text*@</span>)</div>
<div>
<br /></div>
<div>
Twitter Bootstrap is a great tool, and a big time saver as a CSS framework for any new website development project.</div>
<div>
<br /></div>
<div>
Twitter Bootstrap link: <a href="http://twitter.github.io/bootstrap/index.html">http://twitter.github.io/bootstrap/index.html</a></div>
<div>
Font Awesome link: <a href="http://fortawesome.github.io/Font-Awesome/">http://fortawesome.github.io/Font-Awesome/</a></div>
<div>
<br /></div>
<div>
Happy web coding, and building great user experiences!</div>
<div>
<br /></div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com5tag:blogger.com,1999:blog-3151381016744855253.post-88676936393623851412013-02-22T21:06:00.000-08:002013-07-21T17:01:29.076-07:00Specifying Timeout for Azure Storage data retrieval<h3>
Graceful Degradation - handling Windows Azure Storage retrieval failures</h3>
Windows Azure storage can go down any time, as is shown in the worldwide outage today. Luckily this is a very rare event.<br />
<br />
FYI - here's the link to the Windows Azure Service Dashboard:<br />
<a href="http://www.windowsazure.com/en-us/support/service-dashboard/">http://www.windowsazure.com/en-us/support/service-dashboard/</a><br />
<br />
However, in such a situation, any requests from a website that retrieves blob data from the service (images or documents, for example) will cause the website to wait a long time for Azure Storage to respond. As a result, it will take humongous amounts of time before the browser renders the page fully (although with missing images etc). Unacceptable, from a user's point of view.<br />
<br />
In this scenario, we need to gracefully degrade the service from our website. Given the scenario, the best handling we can do is to time-out the Azure Storage request, and then supply an alternative image or other suitable filler. Here's how to time-out the request.<br />
<br />
<h3>
Timing out a Windows Azure Storage blob request</h3>
<br />
<span style="font-family: Courier New, Courier, monospace;"> // Create a timeout for the blob request</span><br />
<span style="font-family: Courier New, Courier, monospace;"> BlobRequestOptions blobRequestOptions = </span><br />
<span style="font-family: Courier New, Courier, monospace;"> new BlobRequestOptions();</span><br />
<span style="font-family: Courier New, Courier, monospace;"> blobRequestOptions.Timeout = TimeSpan.FromMilliseconds(1000);</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> // Retrieve the requested image</span><br />
<span style="font-family: Courier New, Courier, monospace;"> returnContent = </span><span style="font-family: 'Courier New', Courier, monospace;">File(</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> blob.DownloadByteArray(blobRequestOptions), </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> profilePictureMimeType</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> );</span><br />
<div>
<br /></div>
<br />
In the above code, we set a timeout of 1000 milliseconds (1 second). If the Azure Storage service fails to respond within that time, our program will stop waiting, leaving us free to take alternative actions such as serve up a placeholder image. (1 second seems to work with most image files but may still need to be tweaked).<br />
<br />
This way, the website will remain responsive to the user.<br />
<br />
Happy coding!<br />
<br />Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com2tag:blogger.com,1999:blog-3151381016744855253.post-51615152461742053112013-01-26T15:41:00.000-08:002013-07-21T16:59:14.377-07:00Using MVC 4 Model objects inside Javascript for Google maps<br />
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: 'Times New Roman', serif; font-size: 12pt;">Using a server-side object as a Model in an MVC 4 View is quite straightforward. Just define the @model at the top of the .cshtml file, and then use it in the rest of the View as needed, taking advantage of the excellent model binding system in MVC 4.</span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Times New Roman","serif"; font-size: 12.0pt; mso-fareast-font-family: "Times New Roman";">However, using the same Model from within a piece of javascript code is a whole different story. The problem arises because while the rest of the .cshtml file is evaluated on the server side and converted to HTML before being served, the javascript code is sent to the browser and runs on the client side as-is, and does not have access to the Model and its property values.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Times New Roman","serif"; font-size: 12.0pt; mso-fareast-font-family: "Times New Roman";">The solution lies in writing our View such that the Model properties in the .cshtml file are evaluated at the server level itself, and the javascript code simply receives the resulting values and not the property names themselves. Here is a sample code that demonstrates this approach, using the "address" information from a Model object to display a Google Map in the webpage. Using this approach, no changes at all are required in the Controller - the View coding takes care of everything (while not taking over any business logic).<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Times New Roman","serif"; font-size: 12.0pt; mso-fareast-font-family: "Times New Roman";">Assuming that a Layout file is being used to support the View, the steps required to display a Google Map on the webpage are:<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Times New Roman","serif"; font-size: 12.0pt; mso-fareast-font-family: "Times New Roman";">1. In the "head" section of the Layout, create an @RenderSection, so that our main View can insert the javascript into the "head" of the resulting HTML:<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<br />
<pre style="background-color: #f0f0f0; background-image: url(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjZMbcv2SYlTbdBC7wudRcTPkiCyfpFi1uSihPVQMrcP6dOJEmRyf0_RMINIrRJlwzTLyqj_cQDzkCurg81jVhshXkAcmuzQ26fDefptRdt0b3WH4mIO-hb6xq95AsqRGHOZnhJeaYXbMV/s320/codebg.gif); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 450.828125px;"><code style="word-wrap: normal;">@RenderSection("pagespecificcode", required: false) </code></pre>
<br />
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Times New Roman","serif"; font-size: 12.0pt; mso-fareast-font-family: "Times New Roman";"><br /></span>
<span style="font-family: "Times New Roman","serif"; font-size: 12.0pt; mso-fareast-font-family: "Times New Roman";">2. In the "body" section of the Layout, call the initialize() method of our javascript when the onload event fires (be careful with this, as it'll fire off a method of that name in any child page, not just the one you want!):<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br />
<br />
<pre style="background-color: #f0f0f0; background-image: url(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjZMbcv2SYlTbdBC7wudRcTPkiCyfpFi1uSihPVQMrcP6dOJEmRyf0_RMINIrRJlwzTLyqj_cQDzkCurg81jVhshXkAcmuzQ26fDefptRdt0b3WH4mIO-hb6xq95AsqRGHOZnhJeaYXbMV/s320/codebg.gif); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 450.828125px;"><code style="word-wrap: normal;"><body onload="initialize()"> </code></pre>
<br />
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
</div>
<br />
<br />
<span style="font-family: 'Times New Roman', serif; font-size: 12pt;">3. In the .cshtml of our specific View itself:</span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br />
<pre style="background-color: #f0f0f0; background-image: url(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjZMbcv2SYlTbdBC7wudRcTPkiCyfpFi1uSihPVQMrcP6dOJEmRyf0_RMINIrRJlwzTLyqj_cQDzkCurg81jVhshXkAcmuzQ26fDefptRdt0b3WH4mIO-hb6xq95AsqRGHOZnhJeaYXbMV/s320/codebg.gif); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 450.828125px;"><code style="word-wrap: normal;">1: @section pagespecificcode{
2: @*BEGIN - Google Maps calculation and rendering*@
3: <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
4:
5: <script type="text/javascript"
6: src="https://maps.googleapis.com/maps/api/js?sensor=false">
7: </script>
8:
9: <script type="text/javascript">
10: var geocoder;
11: var map;
12: var address = @Html.Raw(Json.Encode(Model.AddrLine1 + ' ' + Model.AddrLine2 + ' ' + Model.AddrCity + ' ' + Model.AddrState + ' ' + Model.AddrPostalCode))
13:
14: @*This function is called from the 'onload' event of the 'body' section of the Layout*@
15: function initialize() {
16: geocoder = new google.maps.Geocoder();
17: var latlng = new google.maps.LatLng(0.0, 0.0);
18: var mapOptions = {
19: zoom: 13,
20: center: latlng,
21: mapTypeId: google.maps.MapTypeId.ROADMAP
22: }
23: map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
24:
25: codeAddress();
26: }
27:
28: function codeAddress() {
29: geocoder.geocode({ 'address': address, 'region': 'US' }, function (results, status) {
30: if (status == google.maps.GeocoderStatus.OK) {
31: map.setCenter(results[0].geometry.location);
32: var marker = new google.maps.Marker({
33: map: map,
34: position: results[0].geometry.location
35: });
36: }
37: });
38: }
39:
40: </script>
41: @*END - Google Maps calculation and rendering*@
42: }
</code></pre>
<div>
<code style="word-wrap: normal;"><br /></code></div>
</div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Times New Roman","serif"; font-size: 12.0pt; mso-fareast-font-family: "Times New Roman";">The crucial line in the code above is the following one; it is taking the Model properties and converting them to a string, which is then set as the value of the javascript variable. One little drawback here - there's no Intellisense available while we're programming the line.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br />
<pre style="background-color: #f0f0f0; background-image: url(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjZMbcv2SYlTbdBC7wudRcTPkiCyfpFi1uSihPVQMrcP6dOJEmRyf0_RMINIrRJlwzTLyqj_cQDzkCurg81jVhshXkAcmuzQ26fDefptRdt0b3WH4mIO-hb6xq95AsqRGHOZnhJeaYXbMV/s320/codebg.gif); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 450.828125px;"><code style="word-wrap: normal;">12: var address = @Html.Raw(Json.Encode(Model.AddrLine1 + ' ' + Model.AddrLine2 + ' ' + Model.AddrCity + ' ' + Model.AddrState + ' ' + Model.AddrPostalCode))
</code></pre>
<div>
<code style="word-wrap: normal;"><br /></code></div>
</div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Times New Roman","serif"; font-size: 12.0pt; mso-fareast-font-family: "Times New Roman";">4. Later on in the .cshtml file, at the point you want to display the map, the only section that's needed to render the map is this - there's no code, just define the div:<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br />
<br />
<pre style="background-color: #f0f0f0; background-image: url(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjZMbcv2SYlTbdBC7wudRcTPkiCyfpFi1uSihPVQMrcP6dOJEmRyf0_RMINIrRJlwzTLyqj_cQDzkCurg81jVhshXkAcmuzQ26fDefptRdt0b3WH4mIO-hb6xq95AsqRGHOZnhJeaYXbMV/s320/codebg.gif); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 450.828125px;"><code style="word-wrap: normal;">1: <div class="gmap" id="map_canvas">
2: </div></code></pre>
<br />
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: "Times New Roman","serif"; font-size: 12.0pt; mso-fareast-font-family: "Times New Roman";">5. Make sure the CSS file defines the correct size and any other properties you want for the rendered map:<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br />
<pre style="background-color: #f0f0f0; background-image: url(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjZMbcv2SYlTbdBC7wudRcTPkiCyfpFi1uSihPVQMrcP6dOJEmRyf0_RMINIrRJlwzTLyqj_cQDzkCurg81jVhshXkAcmuzQ26fDefptRdt0b3WH4mIO-hb6xq95AsqRGHOZnhJeaYXbMV/s320/codebg.gif); border: 1px dashed rgb(204, 204, 204); font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; width: 450.828125px;"><code style="word-wrap: normal;">1: .gmap
2: {
3: width: 340px;
4: height: 180px;
5: border: 1px solid lightgray;
6: } </code></pre>
</div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: 'Times New Roman', serif;"><br /></span>
<span style="font-family: 'Times New Roman', serif;">Done. Now once the Model object is populated by the Controller, the View will take care of the rest and render the Google Map nicely.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: 'Times New Roman', serif;"><b>Caution here</b>: We should not convert the entire object as it creates a large Json string, most of which are likely not needed for our purpose above. They will simply add to your server outbound traffic. Convert only the fields needed, as illustrated above. Checking the Page Source shows exactly what's being converted.<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: 'Times New Roman', serif;">Please note: The above example does not use an API Key from Google. It will work fine without that key; however, Google places limits on how many maps can be rendered, and sites with lots of traffic will need a key. Please see details of the API, the Key, traffic limits and various other aspects of the system on the Google Maps support page: <o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: 'Times New Roman', serif;"><a href="https://developers.google.com/maps/documentation/javascript/tutorial"><span style="color: blue;">https://developers.google.com/maps/documentation/javascript/tutorial</span></a><o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<span style="font-family: 'Times New Roman', serif;">Happy coding!<o:p></o:p></span></div>
<div class="MsoNormal" style="margin-bottom: 0.0001pt;">
<br /></div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com2tag:blogger.com,1999:blog-3151381016744855253.post-85471217703083816252012-10-31T19:57:00.000-07:002013-07-21T17:03:31.908-07:00Optimizing image loading in MVC VS-2012 websitesImages often take up a large proportion of the load time of a webpage, second only to data retrieval from a database. Here are 3 things we can do in our Visual Studio MVC website projects, to <b>significantly </b>reduce that load time.<br />
<br />
<ol>
<li><b>Reduce file size, by controlling the image dimensions and its jpeg quality</b>. Ideally, images should be of exactly the dimensions (width and height in pixels) that are to be finally displayed on the webpage (typically controlled by the css <i>img width</i> and <i>height </i>properties). Also, reducing the jpeg quality (increasing compression) a little may result in a drastic drop in file size while still having very good screen-display quality. If you are using the <i>EncoderParameter </i>class to process images for uploading, consider using a compression level of 90.</li>
<li><b>Enable image caching at <i>both </i>server and browser levels</b>. Use the <i>OutputCache </i>attribute on your controller action method that retrieves the images, and set its <i>location </i>property to "<i>ServerAndClient</i>". The <i>duration </i>property can be set appropriately depending upon your website, and will determine how long your web server and the user's browser are going to cache the image. <i>Added bonus</i>: The server caching will reduce your storage data-out costs, and the browser caching will reduce your web server data-out costs.</li>
<li><b>Eliminate duplicate pulls of the same image, by always using an identical method call pattern</b>. Parameters in an action method are not case-sensitive, nor do they need all the parameters to be included in a call (missing parameters will receive a null). However, when retrieving the same image from different pages or different parts of the same page, inconsistency in the calls from your .cshtml or .vbhtml files will cause your browser to consider these as separate elements, and it will send multiple requests to your server. If the calls are identical, most browsers will send only a single request, and will then cache and re-use the same image. <i>Recommendation</i>: In your @Url.Action() calls, make sure to specify <i>all </i>of the action method's parameters, and treat them as though they were case-sensitive.</li>
</ol>
<div>
Using the above 3 suggestions, you should see significant load time improvements. A good way to do a quick (but pretty detailed) check of the page's performance in the Google Chrome browser: Right-click on the page | click on <i>Inspect Element</i> | click on the <i>Network </i>tab | refresh your page. You'll see exactly what was loaded, and how long each element took. You can click on the <i>Images </i>tab at the bottom, to examine just the images. Then tweak your website using the above suggestions - and look out for improved performance and happier users!</div>
<div>
<br /></div>
<div>
Happy coding!</div>
<div>
<br /></div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-18166574128822418162012-10-29T10:18:00.000-07:002012-10-29T10:21:20.212-07:00Azure .Net 4.5 projects through VS 2012<h2>
Easy Azure deployment of .Net 4.5 websites, using Visual Studio 2012</h2>
Last week, Windows Azure started supporting .Net 4.5. Long awaited, most welcome feature! Now we can create and deploy our .Net 4.5 websites straight to Azure, all from within Visual Studio 2012. No add-ons needed.<br />
<br />
<h4>
In your Azure account:</h4>
<br />
<ol>
<li>Sign-in into your Azure account. If you don't have one, you can always sign-up for a free 3-month trial;</li>
<li>Create a new Azure web host on the Azure server: Click on <b>Web Sites</b> in the left bar, then click on the large <b>+ New</b> button at the bottom;</li>
<li>Select <b>Compute / Website / Quick Create</b> (Note: you may have to sign-up for the Preview program at this point, if not already done so - it's simple, painless and quick);</li>
<li>Give your new Azure web host a name that you like. The URL for your new site will be of the form: <i>http://yournamechoice.azurewebsites.net</i>;</li>
<li>In a minute or 2, the host will be set up, and good to go (to receive your actual web content);</li>
<li>(At this point, if you navigate to the URL, it will show an auto-created default page)</li>
<li>In the main Azure window, click on the name of your new web host (not its URL), to get to its <b>Dashboard </b>page. Click on <b>Download Publish Profile</b>, and note where the Publish Profile was downloaded into your local machine. This will be used by Visual Studio 2012 in the next step.</li>
</ol>
<h4>
In Visual Studio 2012:</h4>
<div>
<ol>
<li>Open up your .Net 4.5 website project, and press Ctrl-F5 to make sure it runs well locally;</li>
<li>Right-click on the website project, and select the <b>Publish...</b> option;</li>
<li>You'll get a new Publish Web window. Click the <b>Import...</b> button, to import the Publish Profile (.PublishSettings file) you obtained earlier from your Azure website host;</li>
<li>You can check everything by using the Next> button, and click Publish when all looks good;</li>
<li>VS 2012 will take just 2 or 3 minutes to upload everything to your Azure host;</li>
<li>At the end of the upload, Visual Studio will automatically bring up your new website, in your default browser.</li>
</ol>
<div>
That's it... done! The whole process has been made very direct and clean - even eliminating the need to add the extra "Windows Azure Cloud Service" project (as had to be done with VS 2010 or with .Net 4.0 websites earlier).</div>
</div>
<div>
<br /></div>
<div>
Happy coding!</div>
<div>
<br /></div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-83954108143228316622012-10-04T16:59:00.000-07:002013-07-21T17:18:16.028-07:00Adding custom fields to a VS2012 MVC4 SimpleMembership system<h3>
Add custom columns/fields to your MVC4 SimpleMembership "UserProfile" table</h3>
<div>
<br /></div>
<div>
<i>(This is the 2nd of a 3-article series, on using SimpleMembership along with customized fields)</i><br />
<i>Article 1: <a href="http://www.dwdmbi.com/2012/09/visual-studio-2012-uses.html">Visual Studio 2012 uses SimpleMembership as its new Membership Provider</a></i><br />
<i>Article 2: This article.</i><br />
<i>Article 3: <a href="http://www.dwdmbi.com/2013/07/vs2012-simplemembership-custom-fields.html">Programatically accessing values in VS2012 SimpleMembership custom fields</a></i><br />
<div>
<i><br /></i></div>
MVC 4 in Visual Studio 2012 introduces the new SimpleMembership Provider as its default web-based authentication system. While this system is much simpler than the earlier ASPNETDB system that it replaces, there definitely are a few new and neat tricks to be learnt.</div>
<div>
<br /></div>
<div>
One of them is: How do I add custom fields to my MVC 4 registration system?</div>
<div>
<br /></div>
<div>
It can be done in just 4 simple steps:</div>
<div>
<br /></div>
<div>
1. Start up VS 2012, and create a new MVC 4 web project. Set up your SimpleMembership registration system - for some help, please see my blog post on <a href="http://www.dwdmbi.com/2012/09/visual-studio-2012-uses.html">setting up a SimpleMembership Provider in a VS2012 MVC4 project</a>.</div>
<div>
<br />
2. Next, run the project and navigate to the login page, so that the tables get created. It is not necessary to create a user at this point.</div>
<div>
<br />
3. Then, in your database, alter the table <b>UserProfile</b>, and add a column: <b>FullName </b>(nvarchar(50)).</div>
<div>
<br />
4. Next, add/reference the new <b>FullName </b>field in the following programs in your project:</div>
<div>
In class <i>UserProfile</i>, in <i>AccountModels.cs</i> (to ensure the class matches the table columns):</div>
<div>
<br />
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> [Table("UserProfile")]</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> public class UserProfile</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> {</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> [Key]</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> public int UserId { get; set; }</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> public string UserName { get; set; }</span></div>
<div>
<span style="color: #6aa84f; font-size: x-small;"><span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: Courier New, Courier, monospace;">// Customization: Field(s) added</span></span></div>
<div>
<span style="color: blue; font-family: Courier New, Courier, monospace; font-size: x-small;"> public string FullName { get; set; }</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
</div>
<div>
<br /></div>
<div>
In class <i>RegisterModel</i>, in <i>AccountModels.cs</i> (to enable handling on the registration form):</div>
<div>
<br /></div>
<div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> public class RegisterModel</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> {</span></div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;"> // Existing code</span></div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;"> // ...</span></div>
<div>
<span style="color: blue; font-size: x-small;"><br /></span></div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;"> // Customization: Field(s) added</span></div>
<div>
<span style="color: blue; font-family: Courier New, Courier, monospace; font-size: x-small;"> [Required]</span></div>
<div>
<span style="color: blue; font-family: Courier New, Courier, monospace; font-size: x-small;"> [Display(Name = "Your full name")]</span></div>
<div>
<span style="color: blue; font-family: Courier New, Courier, monospace; font-size: x-small;"> [DataType(DataType.Text)]</span></div>
<div>
<span style="color: blue; font-family: Courier New, Courier, monospace; font-size: x-small;"> public string FullName { get; set; }</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<br /></div>
</div>
<div>
In the <i>Register.cshtml</i> view (so it'll appear on the user registration form), add a new <span style="font-family: Courier New, Courier, monospace;">li</span> element with the following content:</div>
<div>
<div>
<span style="color: blue; font-family: 'Courier New', Courier, monospace; font-size: x-small;"> @Html.LabelFor(m => m.FullName)</span></div>
<div>
<span style="color: blue; font-family: Courier New, Courier, monospace; font-size: x-small;"> @Html.TextBoxFor(m => m.FullName)</span></div>
<div>
<br /></div>
</div>
<div>
In the [HttpPost] <i>Register </i>ActionResult, in <i>AccountController.cs</i>, modify the call to the <i>CreateUserAndAccount </i>method (to write the new column to your database):</div>
<div>
<div>
<span style="color: #6aa84f; font-family: Courier New, Courier, monospace; font-size: x-small;"> // Customization: Field(s) added when creating a new account</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> WebSecurity.CreateUserAndAccount(</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> model.UserName,</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> model.Password,</span></div>
<div>
<span style="color: blue; font-family: Courier New, Courier, monospace; font-size: x-small;"> new { FullName = model.FullName }</span></div>
<div>
<span style="color: #666666; font-family: Courier New, Courier, monospace; font-size: x-small;"> );</span></div>
</div>
<div>
<br /></div>
<div>
Now run your program and register a new user, supplying a value for the <b>FullName </b>field... success! The new row created in your database table contains the value supplied for the new column!<br />
<div>
<b><br /></b></div>
<div>
<b>Note</b>: This post covers how to <i>create </i>a custom column. Another post explains how to <i>access the value</i> of that column from within your VS 2012 MVC 4 projects. The link is given below.</div>
</div>
<div>
<br /></div>
<div>
<i>Happy coding!</i></div>
<div>
<br />
<i>(This is the 2nd of a 3-article series, on using SimpleMembership along with customized fields)</i><br />
<i>Article 1: <a href="http://www.dwdmbi.com/2012/09/visual-studio-2012-uses.html">Visual Studio 2012 uses SimpleMembership as its new Membership Provider</a></i><br />
<i>Article 2: This article.</i><br />
<i>Article 3: <a href="http://www.dwdmbi.com/2013/07/vs2012-simplemembership-custom-fields.html">Programatically accessing values in VS2012 SimpleMembership custom fields</a></i><br />
<div>
<i><br /></i></div>
</div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com8tag:blogger.com,1999:blog-3151381016744855253.post-32901004305135861932012-09-24T21:09:00.000-07:002013-07-21T17:21:26.295-07:00Visual Studio 2012 uses SimpleMembership as its new Membership Provider<h3>
Simplified approach to implementing Forms Authentication in MVC 4 Web-apps in VS 2012</h3>
<h4>
<div style="font-weight: normal;">
<i><br /></i></div>
<div style="font-weight: normal;">
<i>(This is the 1st of a 3-article series, on using SimpleMembership along with customized fields)</i></div>
<div style="font-weight: normal;">
<i>Article 1: This article</i></div>
<div style="font-weight: normal;">
<i>Article 2: </i><i><a href="http://www.dwdmbi.com/2012/10/adding-custom-fields-to-vs2012-mvc4.html">Adding custom fields to a VS2012 MVC4 SimpleMembership system</a></i></div>
<div style="font-weight: normal;">
<i>Article 3: <a href="http://www.dwdmbi.com/2013/07/vs2012-simplemembership-custom-fields.html">Programatically accessing values in VS2012 SimpleMembership custom fields</a></i></div>
<div>
<br /></div>
</h4>
<h4>
Feature highlights</h4>
<ul>
<li>Schema gets built directly - no more aspnet_regsql.exe (or
aspnet_regsqlazure) needed;</li>
<li>Much simplified as compared to the earlier membership provider;</li>
<li>Usable both for SQL Azure as well as on-premises databases;</li>
<li>Supports OAuth/OpenID, to authenticate users through Facebook, Google etc.</li>
<li>Easy customization: <a href="http://www.dwdmbi.com/2012/10/adding-custom-fields-to-vs2012-mvc4.html">Adding custom fields to your MVC 4 Registration System</a></li>
</ul>
<h4>
The most interesting details of SimpleMembership</h4>
Visual Studio 2012, launched August 15th, uses the new (and very appropriately named) SimpleMembership as its default forms-authentication membership provider for MVC 4 Web Applications, replacing the ASP.NET Membership Provider that all of us have been using for many years. This is a very welcome change. Some of the most interesting changes that we see immediately in this are:<br />
<br />
<ul>
<li>Highly simplified database schema - has just 5 tables, <b><i>no</i></b> Views, and <b><i>no </i></b>stored procedures.</li>
<li>Does not use the aspnet_regsql.exe command line tool to generate the schema. Rather, the tables are generated automatically the first time you add a user into the system (through your application). The database used is whatever you set in the connection string in your web.config file.</li>
<li>The same approach can be used to create the Membership system in Windows Azure, simply by giving the correct connection string in web.config. Since there are no stored procedures and Views to worry about, the system is just a set of tables that is easily synchronizable between your SQL Azure database and your on-premises database.</li>
<li>Another plus for Azure users: now you don't have to worry about the aspnet_regsqlazure tool any more, because the tables are generated by the new system itself.</li>
<li>The UserId is now an <b>int</b> column, not a Guid that was used in the previous ASP.NET Membership system. This simplifies the system even more, and reduces space requirement a bit. Please note, however, that you may have to pay attention to this if you need to merge data from 2 separate implementations of the SimpleMembership provider, else there may be key duplication conflicts.</li>
<li>Supports user authentication through their Facebook, Google, Microsoft Live, Twitter etc accounts, by simple changes in the AuthConfig.cs file in your project (for details, please visit: <a href="http://go.microsoft.com/fwlink/?LinkID=252166">OAuth/OpenID Support for WebForms, MVC and WebPages</a>).</li>
</ul>
<h4>
A quick VS 2012 web project to check out SimpleMembership</h4>
<div>
<ol></ol>
1. Create a new ASP.NET MVC 4 Web Application, and call it, say, SimpleMembershipTest.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9oQ24t54j6qSPIFjfLjISg1bzSrrTjC6fxJDlt1U0G70-clm7rNlXOCR6FT_xdiq7wr6PMlXz5T516-Y_QxcHVQEUCWiZV-CBkCpG8UIm4sef2XqIDg0NagJ4_X6bty60sseSkBozCxQ/s1600/New-MVC4-Web-Application-screen-1.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="112" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9oQ24t54j6qSPIFjfLjISg1bzSrrTjC6fxJDlt1U0G70-clm7rNlXOCR6FT_xdiq7wr6PMlXz5T516-Y_QxcHVQEUCWiZV-CBkCpG8UIm4sef2XqIDg0NagJ4_X6bty60sseSkBozCxQ/s200/New-MVC4-Web-Application-screen-1.JPG" width="200" /></a></div>
<ol></ol>
<div>
2. On the next screen, select Internet Application, and create your new project.</div>
</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBrm8-TVpIpoPE29TjUPIF9MZ17W0RigeXlzsChV5vWqwXyqC9rkUHLU_g3Erv5q-enOfGI4TCSzaq5euILThfo9XITsiCVNNNG9StwYSvlrgF70VZ7wNMbDqzKsOcSBD3IsuYtVwK6zs/s1600/New-MVC4-Web-Application-screen-2.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBrm8-TVpIpoPE29TjUPIF9MZ17W0RigeXlzsChV5vWqwXyqC9rkUHLU_g3Erv5q-enOfGI4TCSzaq5euILThfo9XITsiCVNNNG9StwYSvlrgF70VZ7wNMbDqzKsOcSBD3IsuYtVwK6zs/s200/New-MVC4-Web-Application-screen-2.JPG" width="200" /></a></div>
<div>
<br /></div>
<div>
3. Once the new project is created, it's a good idea to update all your NuGet packages - do this by clicking on <i>Tools \ Library Package Manager \ Manage NuGet Packages for Solution</i>, and then selecting <i>Updates > All</i>. Update each of them one by one by clicking on the Update button. (Note - all the DotNetOpenAuth packages will get updated when you simply update the first one.)</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxC1nGqJEIF7ygr-qC6PgITyd-coML-VUnSCBLQQzFaitJO4OPxBTa6HG7X9APhFuErvC7d4xi5R_H4zGO9HkBIVPS1E4zf9Db5oeGFHu8Xwnkmvea51HNNuH-AQ1BPHfihuWEMcQdz_g/s1600/Updating-NuGet-Packages-in-VS2012.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxC1nGqJEIF7ygr-qC6PgITyd-coML-VUnSCBLQQzFaitJO4OPxBTa6HG7X9APhFuErvC7d4xi5R_H4zGO9HkBIVPS1E4zf9Db5oeGFHu8Xwnkmvea51HNNuH-AQ1BPHfihuWEMcQdz_g/s320/Updating-NuGet-Packages-in-VS2012.JPG" width="320" /></a></div>
<div>
<br /></div>
<div>
4. In the new project's web.config file, change the connection string for DefaultConnection reflect your server, database and relevant db user info. If you specify the "sa" user, even the database itself can be created by your app, however this obviously does not represent best practices.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5tmLLude3incT6zzeRUyMbHnm-j7kfo3b7rI7y2jg_uHgUJIp3yrYUUjwYe6ZtUrW7JnQz0BzrF6HrN6K71gUNVqXG34aPPhf8i2CpryAPa2RXjEsyEFA0Eu3IR6kShOoZ_VmmtFuvRY/s1600/Connection-string-for-using-MVC4-SimpleMembership-schema.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="99" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5tmLLude3incT6zzeRUyMbHnm-j7kfo3b7rI7y2jg_uHgUJIp3yrYUUjwYe6ZtUrW7JnQz0BzrF6HrN6K71gUNVqXG34aPPhf8i2CpryAPa2RXjEsyEFA0Eu3IR6kShOoZ_VmmtFuvRY/s320/Connection-string-for-using-MVC4-SimpleMembership-schema.JPG" width="320" /></a></div>
<div>
<br /></div>
<div>
5. Now press F5 or Ctrl-F5 to run the app, and click the Register button - specify a new user id and password and voila! Your new database schema gets created, along with a data row for the new user! And do notice that it's very lightweight - <i>no SPs and no Views</i>. </div>
<div>
<div>
<br /></div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9MPoitj0ayRemuvoDJiQiPPNN4p5LVHYI_bsqnWoWT3dLnMGGT17v1wAFbUm9zhdTVaFJDayarhfBK4lU7QwQ95uCnI45jJ5t7BT7oxhN7YH6qetmfgEOBdtZ3WOa96OPaE317Vn4x5c/s1600/SimpleMembership-sql-database-schema.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9MPoitj0ayRemuvoDJiQiPPNN4p5LVHYI_bsqnWoWT3dLnMGGT17v1wAFbUm9zhdTVaFJDayarhfBK4lU7QwQ95uCnI45jJ5t7BT7oxhN7YH6qetmfgEOBdtZ3WOa96OPaE317Vn4x5c/s200/SimpleMembership-sql-database-schema.JPG" width="174" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9l9vSKYWcRb59cDJm5lVSUy9ABmUHeas-usOWdqi-34EKFGSxbIPiV0PoMPS-vgPiMYV4oaAK3EPLYuyH3MIu2KrZHWN_eUCnMqDG7akxI3yvADt_j6MsZKvyFymyB2IoaZpFffuthLc/s1600/SimpleMembership-sql-database-diagram.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="154" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9l9vSKYWcRb59cDJm5lVSUy9ABmUHeas-usOWdqi-34EKFGSxbIPiV0PoMPS-vgPiMYV4oaAK3EPLYuyH3MIu2KrZHWN_eUCnMqDG7akxI3yvADt_j6MsZKvyFymyB2IoaZpFffuthLc/s200/SimpleMembership-sql-database-diagram.JPG" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
6. Select data rows from the tables, and you'll see the new user has been created there.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjB9UKzgyNduxfRfAKYqKOwXWIVnS0MzunfpKvydB1BFyzEZ1m6jqE6ojElR2OqgnASKFle6id-117K10ZHHjObebhdPUNYgLpMEBoB7rj0sObeIH_CUByHtadymQ20Fz6dBWNqXLre7kU/s1600/SimpleMembership-sql-user-datarow.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="118" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjB9UKzgyNduxfRfAKYqKOwXWIVnS0MzunfpKvydB1BFyzEZ1m6jqE6ojElR2OqgnASKFle6id-117K10ZHHjObebhdPUNYgLpMEBoB7rj0sObeIH_CUByHtadymQ20Fz6dBWNqXLre7kU/s200/SimpleMembership-sql-user-datarow.JPG" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<i>Happy coding!</i></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<h4>
<div style="font-weight: normal;">
<i>(This is the 1st of a 3-article series, on using SimpleMembership along with customized fields)</i></div>
<div style="font-weight: normal;">
<i>Article 1: This article</i></div>
<div style="font-weight: normal;">
<i>Article 2: </i><i><a href="http://www.dwdmbi.com/2012/10/adding-custom-fields-to-vs2012-mvc4.html">Adding custom fields to a VS2012 MVC4 SimpleMembership system</a></i></div>
<div style="font-weight: normal;">
<i>Article 3: <a href="http://www.dwdmbi.com/2013/07/vs2012-simplemembership-custom-fields.html">Programatically accessing values in VS2012 SimpleMembership custom fields</a></i></div>
<div>
<br /></div>
</h4>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com6tag:blogger.com,1999:blog-3151381016744855253.post-32645523147498638642012-09-19T15:28:00.000-07:002012-10-04T15:29:49.848-07:00Using VSS 2005 with Visual Studio 2012<h2>
It's easy to integrate Visual SourceSafe into Visual Studio 2012</h2>
Visual Studio 2012, launched on 15th Aug 2012, is a terrific IDE. While most of its documentation includes references to using Team Foundation Server (TFS) integration as the source control, it remains very easy to use VSS 2005 instead, by using the VSS plug-in to get a seamless experience.<br />
<br />
This is extremely useful to those of us who have, for years, been using Visual SourceSafe 2005 with Visual Studio 2010, are happy to be in the comfort zone, and find it simpler to stick to VSS until absolutely forced to move.<br />
<h3>
Steps to follow:</h3>
<h4>
A. Select VSS as the plug-in:</h4>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3pYo1sGS9oVPVahBkExGp0hk1oYXYrky_Y6frMGq0oGqHISyQ32V0b_DuVV3Mx9ISaXJhe3lg7v81-oWRB3_XoC7dTdLM58FMt3Rx2GICMhikvaR_RyxiSbwT3t6aGaUIK1C3GnADEl0/s1600/VS2012-Tools-Options-SourceControl-Plugin-selection.JPG" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="187" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3pYo1sGS9oVPVahBkExGp0hk1oYXYrky_Y6frMGq0oGqHISyQ32V0b_DuVV3Mx9ISaXJhe3lg7v81-oWRB3_XoC7dTdLM58FMt3Rx2GICMhikvaR_RyxiSbwT3t6aGaUIK1C3GnADEl0/s320/VS2012-Tools-Options-SourceControl-Plugin-selection.JPG" width="320" /></a></div>
<ol>
<li>Make sure the VSS 2005 client is installed on your machine. If you have been using it with your VS2010 installation, then it's already there.</li>
<li>In VS2012, go to TOOLS > Options</li>
<li>The Options window will come up. In this window, expand "Source Control" on the left pane, and click on "Plug-in Selection". You should see the following choices in the dropdown (which may vary depending on what other source control plugins you have available on your machine): None, Visual Studio Team Foundation Server, Microsoft Visual SourceSafe, and Microsoft Visual SourceSafe (Internet)</li>
<li>Select Microsoft Visual SourceSafe from the dropdown</li>
<li>(Do not click the OK button at this stage)</li>
</ol>
<h4>
B. Next, adjust the VSS environment settings, if necessary:</h4>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv9Rd-i_M2tXWHQX29TYITYizvVlfEcQZrdgWW0Rt3AiHngwg3x55mD_iO8E99wE6QNBQhTqWceZ40cTfeXR7FM9d040zCbqmAyxDSxbJUpjFetJUmHu5emtTc-RO4BlN-GgpLGsmWBcs/s1600/VS2012-Tools-Options-SourceControl-Environment.JPG" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="186" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv9Rd-i_M2tXWHQX29TYITYizvVlfEcQZrdgWW0Rt3AiHngwg3x55mD_iO8E99wE6QNBQhTqWceZ40cTfeXR7FM9d040zCbqmAyxDSxbJUpjFetJUmHu5emtTc-RO4BlN-GgpLGsmWBcs/s320/VS2012-Tools-Options-SourceControl-Environment.JPG" width="320" /></a></div>
<ol>
<li>Under Source Control in the left pane, click on "Environment"</li>
<li>Make sure that "Visual SourceSafe" shows in the dropdown selection in the right pane, then look through all the settings to make sure they are as desired. For most cases, the defaults should be fine.</li>
<li>(Do not click OK even now!)</li>
</ol>
<h4>
C. Finally, make sure the VSS-specific settings are correct:</h4>
</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDm4dgjJrFbLIJa2xOn6sQcqN8vZHzXXfPoPGfXhmBal01lWFaY-20D-3E3KN_0AuNfL8DhMinyfY-FkbNgC0A2Wt_2yALvFNMlCgOQrU-k5G7hht4BjWaezq8jVkng_XizjCptRHcQ_A/s1600/VS2012-Tools-Options-SourceControl-Plugin-Settings.JPG" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="186" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDm4dgjJrFbLIJa2xOn6sQcqN8vZHzXXfPoPGfXhmBal01lWFaY-20D-3E3KN_0AuNfL8DhMinyfY-FkbNgC0A2Wt_2yALvFNMlCgOQrU-k5G7hht4BjWaezq8jVkng_XizjCptRHcQ_A/s320/VS2012-Tools-Options-SourceControl-Plugin-Settings.JPG" width="320" /></a></div>
<ol><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuo9_l7zw1BA1UROHM-3dhvvqq1cagxBA1tVDI9hk1DLQTYeb_b93lri6BuDfAJLd8JCn_5Q5giibbky1RafuWbAvOhGyWA3KJieMlpcJ9Z1Bs_z5ROTAhG-P_T7BeHmE7xzLcDyHiSGM/s1600/VS2012-Tools-Options-SourceControl-PluginSettings-Advanced.JPG" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="199" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuo9_l7zw1BA1UROHM-3dhvvqq1cagxBA1tVDI9hk1DLQTYeb_b93lri6BuDfAJLd8JCn_5Q5giibbky1RafuWbAvOhGyWA3KJieMlpcJ9Z1Bs_z5ROTAhG-P_T7BeHmE7xzLcDyHiSGM/s200/VS2012-Tools-Options-SourceControl-PluginSettings-Advanced.JPG" width="200" /></a>
<li>Under Source Control in the left pane, click on "Plug-in Settings"</li>
<li>Make sure the VSS login id is correct</li>
<li>Click on the "Advanced..." button, and examine each tab of the resulting "SourceSafe Options" window to make sure everything is as expected. Click OK in that window once you're done.</li>
<li>Click OK in the "Options" window, to save your selection.</li>
</ol>
<div>
Specifying VSS 2005 as your source control for VS 2012 is now complete. You should now be able to right-click on any solution in the Solution Explorer, and select "Add Solution to Source Control". This would display the VSS log-on window, and enable you to take it from there.</div>
</div>
<div>
<br /></div>
<div>
Once your solution is in VSS, with the correct settings, you'll find most activities to be highly simplified: Starting to edit a file automatically checks it out, right-clicking gives options such as save changes, compare versions, or undo a checkout, and so on.</div>
<div>
<br /></div>
<div>
<i>Check it out!</i></div>
<div>
<br /></div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com14tag:blogger.com,1999:blog-3151381016744855253.post-1583532305258994762012-03-10T22:31:00.000-08:002012-03-10T22:31:53.993-08:00Visual Studio 11 Beta - installing and getting startedMicrosoft announced the release of the much-anticipated Visual Studio 11 Beta (VS 11) last week. After downloading and installing it today, here are my first impressions.<br />
<br />
<strong>Downloading</strong><br />
The download from MSDN is an ISO (DVD image) file. It's almost 1.8 GB in size, and took a few hours to get (especially as I was simultaneously downloading SQL Server 2012 RC as well -- that one itself being 4.4 GB).<br />
<br />
Next, I unpacked the ISO using 7-Zip, which is available (for free) from download.cnet.com. This way I could run the install directly from the hard disk; the alternative is to burn a DVD and then install Visual Studio 11 from that DVD. Or, if we want to neither unpack nor burn a DVD, 7-Zip also enables us to view the contents of the ISO, and permits running the installer directly from there without unpacking.<br />
<br />
<strong>Installing</strong><br />
Installation was quite straightforward. It took about 2 hours on my machine, and requires about 10 GB of space. The first thing to notice was the new splash screen - stylish in blue and gray. While waiting, there's a cool set of 5 dots that repeatedly move across the screen, replacing the familiar green moving bar. With 2 hours to ponder over them, I concluded that each was pulling the one behind it using a rubber-band. This physics is especially noticeable when the 5th one is pulled away.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdu8TQFmm_vmqWRrX1tID7onlmI3a_2nobyo7fDl-VY0d97AHYGkfsgpN6vxu6ne28mUeKHnGyFVtXHiCXU-XzUpJQTBa6J07oPGpt6236cAS0B6l8nhlMFBkCL8jOze1nXGUowgGKO7M/s1600/VS11Beta_Install_Splashscreen.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdu8TQFmm_vmqWRrX1tID7onlmI3a_2nobyo7fDl-VY0d97AHYGkfsgpN6vxu6ne28mUeKHnGyFVtXHiCXU-XzUpJQTBa6J07oPGpt6236cAS0B6l8nhlMFBkCL8jOze1nXGUowgGKO7M/s320/VS11Beta_Install_Splashscreen.PNG" width="247" /></a></div>
<br />
Once the install is complete, and after a mandatory restart, VS 11 is good to run.<br />
<br />
<strong>Running Visual Studio 11</strong><br />
The first time it's run, it asks for your development environment preferences - C# or VB development, Web development and so on. Along with that, it also offers to copy preferences from the prior VS version - not sure how this overlap works or gets resolved.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6vCMaLRybVUaR79iXeJv9RhV-R87ikIskoyP4vv4l7unQuNV_-KyQyWaPCcZhb47NCq8gyAeVR-utKiZYe4zKiV0hZ2bMmATYnVjVRZwLQocajB8N1G6DIJ_0xNSKp5ARg1axnDLowNk/s1600/VS11Beta_firsttime_start.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6vCMaLRybVUaR79iXeJv9RhV-R87ikIskoyP4vv4l7unQuNV_-KyQyWaPCcZhb47NCq8gyAeVR-utKiZYe4zKiV0hZ2bMmATYnVjVRZwLQocajB8N1G6DIJ_0xNSKp5ARg1axnDLowNk/s320/VS11Beta_firsttime_start.PNG" width="310" /></a></div>
<br />
Once I got past that, the new Visual Studio 11 home screen came up. For Visual Studio 2010 users, the first difference is the switch in placements of the Open/Create Project and the Getting Started materials. The latter is now on the left, much more prominent. In some ways this highlights the available learning material, and that is a good approach. Graphics on the screens are monochrome presently; I imagine the designers at Microsoft are hard at work creating beautiful full-color icons that'll come with the final release.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM8wa1GOaYi0WPNjcPeRYA1r-Ueqmb-TZr7rdZxNLp9Y8xAxIAdpuSNHvLngS78geYwMNJdwju02ayb0x_AVuGby44kfw7QC3CbWOQYsFQjN_gT2xOVNjPSEn0igS8P-Q-mcC-xKr5NFw/s1600/VS11Beta_home_screen.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM8wa1GOaYi0WPNjcPeRYA1r-Ueqmb-TZr7rdZxNLp9Y8xAxIAdpuSNHvLngS78geYwMNJdwju02ayb0x_AVuGby44kfw7QC3CbWOQYsFQjN_gT2xOVNjPSEn0igS8P-Q-mcC-xKr5NFw/s320/VS11Beta_home_screen.PNG" width="320" /></a></div>
<br />
Visual Studio 11 comes with the latest .NET 4.5 framework, as well as with MVC 4. Together, the whole set forms a powerful team, and there's a ton of new material to learn -- as well as to enjoy! They contain great features both on the IDE side as well the .NET library side. It is installable side-by-side with Visual Studio 2010, and in fact one of the most interesting features (first time for any VS version, I think) is that a Visual Studio 2010 .NET 4 project can be opened in VS 11 as well -- without breaking it for VS 2010. This sounds like a really good feature for teams that may have some members using VS 2010 and others VS 11, allowing the team to upgrade gradually.<br />
<br />
Shown below is a screenshot of the default MVC 4 web app, out of the box and without any additional coding at all. It definitely is a very far cry from the VS 2010 MVC 2 or MVC 3 starting points.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXzHbLxxc344KFEyd4Q8hRwWCr8-Jl2eaIkFAv9pUNhkiXFzxSEN1OVep2J82R-pRYfUM3zXRbnXrrlHAFaYEP_Emy4GVfkrOILpJY3tgUgBNGQ4Qfx7WvQ3GsV6kMIYSw_yhtalkl-Ws/s1600/VS11Beta_mvc4_app_homepage.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXzHbLxxc344KFEyd4Q8hRwWCr8-Jl2eaIkFAv9pUNhkiXFzxSEN1OVep2J82R-pRYfUM3zXRbnXrrlHAFaYEP_Emy4GVfkrOILpJY3tgUgBNGQ4Qfx7WvQ3GsV6kMIYSw_yhtalkl-Ws/s320/VS11Beta_mvc4_app_homepage.PNG" width="320" /></a></div>
<br />
Downloading and installing Visual Studio 11 is easy, so don't wait any longer!<br />
<br />
Happy coding!<br />Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-18727393400252626142012-01-28T09:08:00.002-08:002012-03-10T22:38:42.093-08:00Deadlock recovery using TRY-CATCH in SQL Server stored proceduresDeadlocks occur in SQL Server when two separate transactions require exclusive access to the same resources, but each transaction blocks a portion of what is required by the other. In this situation, SQL Server chooses one of them as the 'deadlock victim', and rolls back the victim's transaction while letting the other one continue through.<br />
<br />
The 'victim' will receive error ID 1205, along with a message saying that the transaction was deadlocked on resources with another process and has been chosen as the deadlock victim, and recommending that you rerun the transaction.<br />
<br />
Deadlocking recovery can be handled gracefully within the stored proc, to retry a few times using a TRY-CATCH block. This will first try the transaction, and in case it fails, then loop around to retry it again. Here's a sample code:<br />
<br />
<div class="MsoNormal" style="line-height: normal; margin: 0in 0in 0pt; mso-layout-grid-align: none;">
<span style="color: green; font-family: Consolas; font-size: 9.5pt;">-- Specify how many times to retry this
task</span><span style="font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span></div>
<span style="color: green; font-family: Consolas; font-size: 9.5pt;">-- in case it is selected as the
deadlock victim</span><span style="font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span><br />
<span style="color: blue; font-family: Consolas; font-size: 9.5pt;">DECLARE</span><span style="font-family: Consolas; font-size: 9.5pt;"> <span style="color: teal;">@retry</span> <span style="color: blue;">INT</span><span style="color: grey;">;</span><o:p></o:p></span><br />
<span style="color: blue; font-family: Consolas; font-size: 9.5pt;">SET</span><span style="font-family: Consolas; font-size: 9.5pt;"> <span style="color: teal;">@retry</span> <span style="color: grey;">=</span> 5<span style="color: grey;">;</span><o:p></o:p></span><br />
<br />
<span style="color: blue; font-family: Consolas; font-size: 9.5pt;">WHILE </span><span style="color: grey; font-family: Consolas; font-size: 9.5pt;">(</span><span style="color: teal; font-family: Consolas; font-size: 9.5pt;">@retry</span><span style="font-family: Consolas; font-size: 9.5pt;"> <span style="color: grey;">></span> 0<span style="color: grey;">)</span><o:p></o:p></span><br />
<span style="color: blue; font-family: Consolas; font-size: 9.5pt;">BEGIN</span><span style="font-family: Consolas; font-size: 9.5pt;"><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">BEGIN</span> <span style="color: blue;">TRY</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><span style="color: blue;">BEGIN</span> <span style="color: blue;">TRANSACTION</span><span style="color: grey;">;</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><span style="color: green;">-- The body of the stored procedure, containing </span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><span style="color: green;">-- various INSERT/UPDATE/DELETE/SELECT statements</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><span style="color: green;">-- Statements</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><span style="color: green;">-- More statements</span><o:p></o:p></span><br />
<br />
<div class="MsoNormal" style="line-height: normal; margin: 0in 0in 0pt; mso-layout-grid-align: none;">
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><span style="color: green;">-- If we reach here without problems, we are good to go</span><o:p></o:p></span></div>
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><span style="color: green;">-- Set the loop variable to 0 so we can exit out, and
COMMIT</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><span style="color: blue;">SET</span> <span style="color: teal;">@retry</span> <span style="color: grey;">=</span> 0<span style="color: grey;">;</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 2;"> </span><span style="color: blue;">COMMIT</span> <span style="color: blue;">TRANSACTION</span><span style="color: grey;">;</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 1;"> </span><span style="color: blue;">END</span> <span style="color: blue;">TRY</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 1;"> </span><span style="color: blue;">BEGIN</span> <span style="color: blue;">CATCH</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">IF</span> <span style="color: magenta;">XACT_STATE</span><span style="color: grey;">()</span> <span style="color: grey;"><></span> 0<o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">ROLLBACK</span> <span style="color: blue;">TRANSACTION</span><span style="color: grey;">;</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: green;">-- Retry again if this is the deadlock victim</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">IF </span><span style="color: grey;">(</span><span style="color: magenta;">ERROR_NUMBER</span><span style="color: grey;">()</span> <span style="color: grey;">=</span> 1205<span style="color: grey;">)</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">SET</span> <span style="color: teal;">@retry</span> <span style="color: grey;">=</span> <span style="color: teal;">@retry</span> <span style="color: grey;">-</span> 1<span style="color: grey;">;</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">ELSE</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">SET</span> <span style="color: teal;">@retry</span> <span style="color: grey;">=</span> <span style="color: grey;">-</span>1<span style="color: grey;">;</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: green;">-- If not retrying again, log the error</span></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="color: green;"><span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"><span style="color: black;"> </span></span><span style="color: green;">-- (Removing the IF statement below will cause each retry to be logged as well)</span></span></span></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="color: green;"><span style="font-family: Consolas; font-size: 9.5pt;"><span style="color: green;"></span></span></span></span><span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">IF</span> <span style="color: teal;">@retry</span> <span style="color: grey;"><=</span> 0<o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">BEGIN</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 3;"> </span><span style="color: blue;">INSERT</span> <span style="color: blue;">INTO</span> <span style="color: teal;">ErrorLog</span><o:p></o:p></span><br />
<span style="color: blue; font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 4;"> </span></span><span style="color: grey; font-family: Consolas; font-size: 9.5pt;">(</span><span style="color: teal; font-family: Consolas; font-size: 9.5pt;">SourceProgram</span><span style="color: grey; font-family: Consolas; font-size: 9.5pt;">,</span><span style="font-family: Consolas; font-size: 9.5pt;"> <span style="color: teal;">SourceLine</span><span style="color: grey;">,</span> <span style="color: teal;">ErrorNumber</span><span style="color: grey;">,</span> <span style="color: teal;">ErrorMessage</span><span style="color: grey;">)</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 3;"> </span><span style="color: blue;">VALUES</span><o:p></o:p></span><br />
<span style="color: blue; font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 4;"> </span></span><span style="color: grey; font-family: Consolas; font-size: 9.5pt;">(</span><span style="color: magenta; font-family: Consolas; font-size: 9.5pt;">ERROR_PROCEDURE</span><span style="color: grey; font-family: Consolas; font-size: 9.5pt;">(),</span><span style="font-family: Consolas; font-size: 9.5pt;"> <span style="color: red;">'Line: '</span>
<span style="color: grey;">+</span> <span style="color: magenta;">CONVERT</span><span style="color: grey;">(</span><span style="color: blue;">varchar</span><span style="color: grey;">,</span> <span style="color: magenta;">ERROR_LINE</span><span style="color: grey;">()),</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 4;"> </span><span style="mso-spacerun: yes;"> </span><span style="color: magenta;">ERROR_NUMBER</span><span style="color: grey;">(),</span> <span style="color: magenta;">ERROR_MESSAGE</span><span style="color: grey;">());</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-spacerun: yes;"> </span><span style="color: blue;">END</span><o:p></o:p></span><br />
<span style="font-family: Consolas; font-size: 9.5pt;"><span style="mso-tab-count: 1;"> </span><span style="color: blue;">END</span> <span style="color: blue;">CATCH</span><o:p></o:p></span><br />
<span style="color: blue; font-family: Consolas; font-size: 9.5pt;">END</span><span style="font-family: Consolas; font-size: 9.5pt;"> <span style="color: green;">-- End WHILE loop.</span><o:p></o:p></span><br />
<br />
Happy coding!Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-89870010625087507592011-01-22T23:35:00.000-08:002012-10-04T11:12:10.716-07:00Let your database applications happily traverse timezonesSometimes developers come across the situation where a datetime value is stored on each row in a table (for example, the created or last-modified datetime), and then subsequently this datetime value needs to be accessed and processed in some way by a client program. If the client program is such that it may be running in any arbitrary timezone (e.g. web users), it becomes difficult or impossible to be sure about when exactly the underlying row was created or modified. A standardized approach, using the UTC (Coordinated Universal Time), is required.<br />
<br />
<br />
<strong>On the SQL Server side:</strong> <br />
<ul>
<li>When storing a datetime in SQL Server, always generate the datetime in the SQL stored proc itself (in the INSERT or UPDATE statement); never pass it from the calling client program.</li>
<li>Always store the datetime as UTC and not local -- use the GETUTCDATE() function in SQL Server, not GETDATE()</li>
<li>For comparisons of current datetime against the datetime on a given row, again ONLY use the current UTC time generated by the SQL Server and not by the client app. To get the current server UTC time, you can create a simple stored proc that just returns the GETUTCDATE() value, perhaps as an OUTPUT parameter.</li>
</ul>
<strong>On the .NET client side:</strong><br />
<br />
<ul>
<li>Use DateTime.Compare method to compare the datetime values received from SQL Server</li>
<li>If you need to convert a UTC time returned by the server into the local time, for display purposes, use the DateTime.ToLocalTime method in your client application. This also automatically factors-in any Daylight Saving time rule in effect.</li>
</ul>
Observing these simple rules will ensure that the date/time comparisons remain valid and accurate in your database applications, whether they are deployed over local networks or over the Internet across the globe. A positive side-effect is also that if you move your server to a different timezone, the logic to handle UTC datetimes needs no change at all - just make sure the server system clock is set to its local timezone.<br />
<br />
Happy coding!<br />
<br />
PS: Why is "Coordinated Universal Time" abbreviated to UTC and not to CUT? Check out these links:<br />
<br />
<a href="http://www.nist.gov/pml/div688/utcnist.cfm">http://www.nist.gov/pml/div688/utcnist.cfm</a><br />
<br />
<a href="http://en.wikipedia.org/wiki/Utc#Symbol">http://en.wikipedia.org/wiki/Utc#Symbol</a>.<br />
<br />
<div>
</div>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-87420828249209524872010-12-29T23:57:00.000-08:002012-10-04T11:12:52.264-07:00Use (UPDLOCK, READPAST) to read a SQL Server data queue<strong>The problem</strong><br />
A useful business scenario involves reading data from a "queue", which would be a table consisting of rows generated by one process, but read by a separate process that runs independently. The second process (queue reader) may be running with multiple threads, or having multiple instances (such as a multi-user desktop or web application). This can often lead to unexpected results, such as duplicate data retrieval, deadlocks, or other concurrency issues. The SQL Server stored procedure called by such an application may involve a set of steps similar to:<br />
<ol>
<li>SELECT the ID of a specific row (meeting some criteria) into a temporary variable</li>
<li>UPDATE a flag on that row to mark it as "processed"</li>
<li>SELECT the data from that row along with rows from any related tables</li>
</ol>
Enclosing the above logic inside a BEGIN TRANSACTION - COMMIT TRANSACTION segment does not prevent any parallel processes from reading the same ID in step-1 above. This is because while the transaction prevents both processes from trying to update the same row, but the SELECT in step-1 of one transaction does not block the other transaction from also doing the same SELECT.<br />
<br />
<strong>The solution</strong><br />
An efficient approach to solving this is to use the WITH (UPDLOCK, READPAST) table hint in the SELECT statement. UPDLOCK specifies that update locks are to be taken and held until the transaction completes, and READPAST specifies that SQL Server not read rows (i.e. skip them) that are locked by other transactions.<br />
<br />
The combined effect of these table hints is that the first transaction to get to the SELECT statement establishes a lock on the row (or page), and the second transaction simply skips the locked rows and reads the next available row. Without the READPAST, the second transation would get blocked by the first one until the locks are released.<br />
<br />
Here's a sample code segment to use these table hints in a stored procedure:<br />
<br />
<span style="font-family: inherit;"></span> <br />
<blockquote>
<span style="font-family: inherit;">BEGIN TRANSACTION</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">SELECT TOP 1 @rowid = a.RowID</span><br />
<span style="font-family: inherit;">FROM TestTable a WITH (UPDLOCK, READPAST)</span><br />
<span style="font-family: inherit;">ORDER BY a.ProcessedOnUtc, a.RowID</span><br />
<br />
<span style="font-family: inherit;"></span><br />
<span style="font-family: inherit;">UPDATE TestTable SET ProcessedOnUtc = GETUTCDATE() </span><br />
<span style="font-family: inherit;">WHERE RowID = @rowid</span><br />
<br />
<span style="font-family: inherit;"></span><br />
<span style="font-family: inherit;">-- (rest of the data retrieval and processing statements) </span><br />
<span style="font-family: inherit;"> </span><br />
<span style="font-family: inherit;">COMMIT TRANSACTION </span></blockquote>
<br />
<strong>A very important Gotcha!</strong> <br />
To enable this to work correctly, there must be an index on the column(s) being used to identify the selected row(s), in this case the RowID column. Without that index, SQL Server will need to look through the table to find the correct row for the second transaction, which will then get blocked since it won't be able to get past the row that's locked by the first transaction. The result in such a case will be that duplicates will be avoided but deadlocks will still occur - the same as using UPDLOCK without READPAST.Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com1tag:blogger.com,1999:blog-3151381016744855253.post-304586743311903762010-12-27T21:02:00.000-08:002012-10-04T11:13:34.851-07:00Setting up a software RAID-5 on a Dell T310 serverGot the opportunity to wet my feet on setting up a software-based RAID-5 on a Dell PowerEdge T310 server, within Windows Server 2008. Quite an experience!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<strong>The hardware</strong><br />
The system was originally equipped with a 250GB drive. It contains the OS and we left it alone. Installed 3 additional, identical, drives: 1TB Western Digital Caviar Black. These are 7200 RPM drives with 32 MB buffers. The question was whether to go for the RE4 drives (also from WD), but those are about twice as costly, and this is a medium load dev & test server, so it was decided to stick with the Blacks and save some Green.<br />
<br />
<strong>Drive installation</strong><br />
This was somewhat unexpectedly tricky. The physical installation was a breeze of course - just pull out the drive trays, set the drives into them, push them back in and snap in the connectors. But, the system refused to boot after this - LED indicators 1 and 4 lit up and that was it; the system did not even get to the BIOS setup screen.<br />
<br />
After some initial irritation ("a drive must be DOA!"), we tried out the drives in a different machine (desktop, not server); all worked perfectly. Good. Then, a bit of research into the owner's manual for the T310 showed that there's a jumper on the motherboard that clears the stored configuration when the system is next restarted. Set the jumper accordingly, plugged-in and restarted the system, and... no luck! What! Restored the jumper, tried again, same results. Again toyed with the jumper; nope. I think the fourth time around I saw what initially looked like an unbelievable dream - various LED lights flashing back and forth, the disk light flashing, and generally speaking a sight very different from the steady 1 and 4 indicators! Scrambled to press F2 to get into the setup screen. The drives showed up fine.<br />
<br />
There must be something that I missed here. Drive installation would not be such a hit-or-miss approach. Besides, this would tend to disprove the theory that trying the same thing repeatedly and expecting success is madness.<br />
<br />
<strong>Setting up the RAID-5 in Windows Server 2008</strong><br />
Once Windows booted up, went into Start > Computer Management, and clicked on Disk Management (under the Storage subsection). The 3 additional drives showed up fine, although unformatted and unavailable for use at this point. In Windows Explorer, they are not showing up yet.<br />
<br />
Right-clicked on one of the new drives in the Disk Management UI screen, and was immediately presented with an option to set up a RAID-5. Selected that option led to further screens where the remaining 2 drives were added into the RAID, and that was about it. Once the new drive name and letter were specified, the system started to sync the 3 drives together, to form a 2TB volume using the 3x1TB drives.<br />
<br />
Note that the system can set up the drives as RAID-0, RAID-1 or RAID-5; available options will depend on how many drives are available (with just 2 drives, the RAID-5 option will be grayed-out).<br />
<br />
Another important note when setting up a RAID-5: you cannot have data on a drive, and then add 2 more drives and try to get them all into a RAID-5 configuration (though you can mirror the original drive). All the 3 drives must be empty when you start.<br />
<br />
Once we gave the go-ahead to start the sync, the system takes a long, long time! This is the progress after about 7 hours:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAzv0q0RuO_6Gv_i9a872PAUr4slQj1GGWDEOoSkFjWheSRSx_CYFLWeqykfmm9hQtrp5iAg5VFTjRQiEoKz8rtl_cC2Ko9dSBxJi0aiHK9kZBlRgp0zm5NBLP0k02blr4I1rBnVu2qOI/s1600/RAID5+progress.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="205" n4="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAzv0q0RuO_6Gv_i9a872PAUr4slQj1GGWDEOoSkFjWheSRSx_CYFLWeqykfmm9hQtrp5iAg5VFTjRQiEoKz8rtl_cC2Ko9dSBxJi0aiHK9kZBlRgp0zm5NBLP0k02blr4I1rBnVu2qOI/s320/RAID5+progress.JPG" width="320" /></a></div>
<br />
Interestingly, while the sync is going on, I can work with the drives - they show up as a single 2TB volume in Windows Explorer (E: in this case). I tried creating a simple Notepad document and it seems to be fine. However, I'm going to wait for the entire sync to complete before loading any real data on the volume.<br />
<br />
Once done, this will give me 2TB of space, and a little peace of mind against disk crashes.<br />
<br />Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-88482655036102127232010-10-11T14:44:00.000-07:002010-10-11T14:52:12.593-07:00Data Mining rated as a "hot" career choice by UCSD ExtensionThe University of California San Diego (UCSD) Extension, in its study called "Hot Careers for College Graduates 2010" gives top marks to Data Mining as a career choice for recent and mid-career college graduates. Its hottest 10 career options are listed below. The top 4 careers continue to remain in computers (inspite of the significant outsourcing of software development jobs overseas).<br />
<br />
1. Healthcare Information Technology<br />
2. Mobile Media<br />
3. Data Mining<br />
4. Embedded Engineering<br />
5. Geriatric Healthcare<br />
6. Occupational Health & Safety<br />
7. Spanish/English Translation & Interpretation<br />
8. Sustainable Business Practices & Greening of all Jobs<br />
9. Feature Writing for the Web<br />
10. Teaching English as a Foreign Language<br />
<br />
<strong>URLs for the study</strong><br />
Brief summary: <a href="http://extension.ucsd.edu/about/display/dsp_release.cfm?vAction=view&vEventID=566">http://extension.ucsd.edu/about/display/dsp_release.cfm?vAction=view&vEventID=566</a><br />
Detailed paper: <a href="http://extension.ucsd.edu/about/images/careerReport.pdf">http://extension.ucsd.edu/about/images/careerReport.pdf</a><br />
<br />
<strong>A quote from the summary</strong><br />
<blockquote>
"Looking for a needle in a haystack is a good analogy for data mining jobs. Data mining is the technique of extracting specific types of information or patterns from large databases, such as data warehouses. Advanced statistical methods sift through large volumes of data, providing answers to questions that were once too time-consuming."</blockquote>
<strong>A quote from the detailed PDF document</strong><br />
<blockquote>
"More importantly, data mining is a rapidly growing industry due to the explosion of available data. A study by students and faculty at the University of California Berkeley found that the amount of data in the world doubles every three years. For this reason, more employees are needed in the data mining industry to drill down, analyze and interpret the data.<br />
<br />
"Career prospects exist in several areas, such as advertising technology, fraud detection, surveillance, web mining, probabilistic trading, risk management, business intelligence, scientific research and law enforcement. Data mining requires comprehension of algorithms and advanced statistics, and the ability to program and use advanced software. Job hunters with computer science and statistics training, along with good business sense, would be well-suited to this career. Individuals with an understanding of the appropriate math and computer science would also be well qualified.<br />
<br />
"Data Mining Analysts, Data Mining Researchers, Data Mining Scientists and other Data Mining professionals can expect to earn high wages."</blockquote>Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com1tag:blogger.com,1999:blog-3151381016744855253.post-68965681601346150642010-08-18T23:09:00.000-07:002010-08-19T06:26:25.104-07:00SSIS 2008 Transforms: Term Extraction and Term LookupSSIS 2008 includes a very good lineup of transformation task components. In this article, we take a brief look at a pair of very interesting, related transforms that help us <strong>analyze unstructured data</strong>: Term Extraction and Term Lookup.<br />
<br />
<strong>The Term Extraction transform</strong><br />
Have you ever had the need to extract the nouns and noun phrases from a text column (or a text file)? And when writing that long program to do it, did you wish there was a simpler way? If the answers are "yes" and "yes", then the Term Extraction transform is your friend! This transform analyzes free-flowing text in your data source, and enables you to pull out nouns and noun phrases that are found in it, along with the frequencies with which they occur.<br />
<br />
Advanced settings for the Term Extraction transform enable you to select just 1-word nouns ("SQL"), noun phrases ("SQL Server 2008"), or both nouns and noun phrases. The count itself can be just a simple count of the frequency of occurrence, or computed using a formula called TFIDF, which stands for Term Frequency and Inverse Document Frequency. The TFIDF calculation delivers a weighted result, based upon the frequency of the word or phrase, number of rows in which it occurs, and the total number of rows in the data. Try out both approaches, to see which one best meets your needs.<br />
<br />
<strong>The Term Lookup transform</strong><br />
Term Lookup also breaks up incoming text into nouns and noun phrases; however, it then compares these to a set of user-defined terms and delivers the frequency counts of only the matching ones, on a row by row basis. So, once you have the list of terms from the earlier (Term Extraction) transform, you can review and edit that list and retain only the ones of interest, and then run the Term Lookup transform against that list to get counts of the terms matched on each row.<br />
<br />
Since the results of this transform are displayed at row-level detail, a given row may occur multiple times in the results if it contains multiple flagged terms. If you need to group the results by the term being matched, you'll need to use the Aggregate Transform in conjunction with the Term Lookup transform.<br />
<br />
<strong>Using these powerful transforms to analyze unstructured data</strong><br />
For an actual project, it could be of great value to run the Term Extract transform initially and then once in a while, and have a domain expert or Business Analyst review the results and edit the list. That list then is used by the Term Lookup transform, which would run regularly, perhaps weekly. As a practical example, take the case of the Customer Support call-in system of a packaged software manufacturer. When logging a support call, the technician will typically select the product line and application module, perhaps select a generic pre-programmed problem code from a dropdown, and then enter a few words of free-format text into a field, noting the problem as described by the customer as well as some comments etc. Over time, the database may consist of millions of rows. Using this pair of transforms on that free-format field, management will be able to narrow down the data to the most frequently encountered problems within each product line, thereby getting a clearer view of the areas to focus on. Powerful results - from unstructured data!Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-8670832428049140352010-08-04T11:19:00.000-07:002010-08-18T23:24:56.799-07:00The Dimensional Model Star Schema - a quick overview<strong>Data organization - DW vs. OLTP</strong><br />
<br />
The tables in a DW are organized precisely opposite to how they are done in a regular OLTP database - highly <b>de-normalized</b> in a DW vs. highly <b>normalized</b> in an OLTP system. The reason is that their objectives are different. A DW system is read-only and built for fast queries on aggregated data, while an OLTP system is read-write and built for fast Create-Retrieve-Update-Delete (CRUD) operations on individual transactions.<br />
<br />
Database developers are used to living and breathing normalized systems. Therefore for them, when transitioning to BI design, it becomes most important to think in terms of de-normalizing everything!<strong></strong><br />
<br />
<strong>Dimensional modelling - Fact and Dimension tables</strong><br />
<br />
<span style="background-color: white;">Data in a DW is organized into tables that are called Fact and Dimension tables. The fact tables contain the actual data (often called <strong>measures</strong>), which is typically numeric and can be aggregated, such as the quantity and unit price of line-items from a sale invoice. Dimension tables, on the other hand, contain the <strong>attributes</strong> of that data - the date of sale, item sold, customer name and address, and so on. Foreign keys on the fact tables link to the primary keys on the dimension tables. A Data Warehouse can have as few as 1 fact table and 1 dimension table.</span><br />
<br />
Fact tables run deep - millions or sometimes even billions of rows. Dimension tables are very wide (remember de-normalization!) but not deep - lots of columns but generally maxing out at 1 or 2 million rows.<br />
<br />
The arrangement of the fact and dimension tables is called the Star Schema. To visualize the star, think of the fact table sitting at the center, and lines radiating from it that connect to each dimension. The instant advantage of the star schema is that each fact table row is just 1 join away from any of its dimensions (though a <strong>snowflake</strong> dimension is an occasionally-used exception), resulting in super-fast query results. A query asking for data on sales of Mountain Bikes in California during the last calendar year will likely look at only 3 dimension tables, with each being linked directly to the fact table. But the same query in the OLTP system, because of normalization, is going to hit a much greater number of joins in relational tables (state - customer address - customer id - invoice header - invoice detail, to determine just the California sales alone).<br />
<br />
Data in the DW is not real-time. It is loaded on a regular schedule from the OLTP system (and even other non-relational sources such as flat files and spreadsheets etc), often nightly, and often by using an ETL tool such as SSIS which can be scheduled to run automatically.Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-8977243142802182162010-07-29T08:12:00.000-07:002012-10-04T11:14:13.221-07:00The DW-DM-BI acronym and terminology guideThe BI field is full of acronyms - many are common to the industry itself, while others are specific to the Microsoft solution (incidentally, Microsoft used to be famous for their TLAs - Three Letter Acronyms - but lately we are seeing some increased usage of four letter acronyms).<br />
<br /><br /><table><thead align="left">
<tr><th>Terminology</th><th>Description</th></tr>
</thead><tbody valign="top">
<tr><td>AMO</td><td>Analysis Management Objects</td></tr>
<tr><td>BI</td><td>Business Intelligence</td></tr>
<tr><td>BIDS</td><td>Business Intelligence Development Studio</td></tr>
<tr><td>Cube</td><td>A logical object that stores dimensions and facts and provides a multidimensional view of the data (often used interchangeably with <em>UDM</em>)</td></tr>
<tr><td>DM</td><td>Data Mining; also used for Data Mart</td></tr>
<tr><td>DMX</td><td>Data Mining Extensions (language)</td></tr>
<tr><td>DTS</td><td>Data Transformation Services (generally refers to legacy SQL Server 2000 packages)</td></tr>
<tr><td>DW</td><td>Data Warehousing, or a Data Warehouse</td></tr>
<tr><td>ETL</td><td>Extract, Transform, Load</td></tr>
<tr><td>HOLAP</td><td>Hybrid OLAP</td></tr>
<tr><td>KPI</td><td>Key Performance Indicator</td></tr>
<tr><td>MDX</td><td>Multidimensional Expressions (language)</td></tr>
<tr><td>MOLAP</td><td>Multidimensional OLAP</td></tr>
<tr><td>OLAP</td><td>Online Analytical Processing (also see HOLAP, MOLAP, ROLAP)</td></tr>
<tr><td>OLTP</td><td>Online Transaction Processing</td></tr>
<tr><td>ROLAP</td><td>Relational OLAP</td></tr>
<tr><td>SSAS</td><td>SQL Server Analysis Services</td></tr>
<tr><td>SSIS</td><td>SQL Server Integration Services</td></tr>
<tr><td>SSRS</td><td>SQL Server Reporting Services</td></tr>
<tr><td>UDM</td><td>Unified Dimensional Model (often used interchangeably with <em>cube</em>)</td></tr>
<tr><td>XMLA</td><td>XML for Analysis</td></tr>
</tbody></table>
Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0tag:blogger.com,1999:blog-3151381016744855253.post-42688636407549240632010-07-28T18:21:00.000-07:002010-08-18T23:19:43.307-07:00Use a Data Warehouse for Reporting<blockquote></blockquote><blockquote></blockquote>Businesses of all types and sizes use database systems, called Online Transaction Processing (OLTP) systems, to store and manage their transactions. These OLTP systems, when well designed, store data in relational tables in a highly <b>normalized </b>format, that is, tables are linked to each other so as to: <br />
<ul><li>Avoid duplication/redundancy of data</li>
<li>Minimize space requirements</li>
<li>Enable fast retrieval and updates of these transactions</li>
</ul>OLTP systems are very efficient at storing millions of transactions. However, when building reports that need to aggregate this data (for example, calculating sum, average, percentage etc), things start to slow down. The problem becomes more pronounced as we start to filter on various criteria, such as "Total $ amount of sales for last year", or "Total $ amount of sales of Mountain Bikes in California during Q3 of last year".<br />
<br />
This is because aggregation is being done <b>on the fly for each query</b>, and combined with the additional filters (WHERE clauses), quickly starts eating up our SQL Server resources. Not only are the reports slow, but other business users experience significant system slowdowns as well.<br />
<br />
A Data Warehouse (DW) on the other hand, which is part of an Online Analytical Processing (OLAP) system, is built for one purpose alone - fast, efficient reporting.<br />
<br />
In a DW system, data from the OLTP system will be read, de-normalized, and (typically) stored in a Star Schema, as a set of Dimension and Fact tables. This is often done using SQL Server Integration Services (SSIS). From these tables, tools within SQL Server Analysis Services (SSAS) can be used to build <b>cubes</b>, which contain not only our data but also its pre-calculated aggregates based upon definitions that we specify within SSAS. This results in many advantages:<br />
<ul><li>Super-fast reporting </li>
<li>No impact on the OLTP system</li>
<li>Data Mining (DM) and Business Intelligence (BI) solutions can be readily built</li>
</ul>The DW is not synchronized in real-time with the OLTP system. It is loaded at regular intervals, which may be as varied as once a month or several times a day, depending on the business requirements. SSIS and SSAS enable us to create packages that can be scheduled to run automatically (say, nightly), simplifying the time-consuming activities of loading of the Data Warehouse and building the Cubes.Krishna Guptahttp://www.blogger.com/profile/08263412563703860802noreply@blogger.com0