Category: ASP.NET MVC

MVC Zip Result

Sometimes you need to send to the user more than 1 file – or, maybe, the file is too large
The simplest way is : made a zip file that contains the others.

What do you need
1. SharpzipLib from http://www.icsharpcode.net/opensource/sharpziplib/ ( or download via NuGet in VS)
2. obtain the file(s) that you want as a string or as a byte[] – let’s say you have a byte[] to store in a str variable
3. make in your action something like that:

 var fcr = new ZipResult("Export.xls", str);
            fcr.AddFile("readme.txt","this zip file contains ..");
            return fcr;

4. copy the following code in your asp.net mvc projects:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using System.Text;

namespace utilsResult
{
    public class ZipResult : FileResult
    {

        
        private Dictionary<string, byte[]> content = new Dictionary<string, byte[]>();
        public string FileNameZip;
        public ZipResult(string FileName, byte[] Contents)
            : base("application/octet-stream")
        {
            this.FileDownloadName = Path.GetFileNameWithoutExtension(FileName) + ".zip";
            AddFile(FileName, Contents);
        }
        public void AddFile(string FileName,  byte[] Contents)
        {
            content.Add(FileName, Contents);
        }
        public void AddFile(string FileName,string Contents, Encoding e = null)
        {
            if (e == null)
                e = ASCIIEncoding.ASCII;

            content.Add(FileName, e.GetBytes(Contents));
        }

        protected override void WriteFile(HttpResponseBase response)
        {

            using (ZipOutputStream zos = new ZipOutputStream(response.OutputStream))
            {
                zos.SetLevel(3);
                zos.UseZip64=UseZip64.Off;

                foreach (var item in content)
                {
                    ZipEntry ze = new ZipEntry(item.Key);
                    ze.DateTime = DateTime.Now;                    
                    zos.PutNextEntry(ze);
                    int count=item.Value.Length;
                    zos.Write(item.Value, 0, count);
                   
                    
                }                
            }
        }
    }
}

5. future improvements:
Zip the file(s) in a dll project to made fully testable!

ASP.NET MVC make users–roles fast

please left click on “Microsoft SQL Server Management Studio (push button)”
clip_image001
please create a database in “Microsoft SQL Server Management Studio”
clip_image002
please left click on “C:\Windows\system32\cmd.exe (push button)” and enter the following command : aspnet_regsql.exe -E -S .\SQLExpress -d proprii -A rm
clip_image003
please left click on “ListPanel (list)” in “Microsoft SQL Server Management Studio”
clip_image004
please left click on “Tables (outline item)” in “Microsoft SQL Server Management Studio”
clip_image005
please keyboard input in “Microsoft SQL Server Management Studio” [F5]
clip_image006
please right click on “aspnet_Applications (list item)” in “Microsoft SQL Server Management Studio”
clip_image007
please left click on “Select Top 1000 Rows (menu item)”
clip_image008
please left click in “Microsoft SQL Server Management Studio”
clip_image009
please left click on “aspnet_Users (list item)” in “Microsoft SQL Server Management Studio”
clip_image010
please right click on “aspnet_Users (list item)” in “Microsoft SQL Server Management Studio”
clip_image011
please left click on “Select Top 1000 Rows (menu item)”
clip_image012
please left click on “RdcDAL – Microsoft Visual Web Developer 2010 Express (Administrator) (push button)”
clip_image013
please edit web.config in your MVC application and change the connection string to point to your database
clip_image014
please scroll down until membership and change application name from / to /applicationName
clip_image015
please run your application and left click on “Log On (editable text)” in “Home Page – Windows Internet Explorer”
clip_image016
please left click on “User name (editable text)” in “Log On – Windows Internet Explorer”
clip_image017
please left click on “Register (editable text)” in “Log On – Windows Internet Explorer”
clip_image018
please left click on “User name (editable text)” in “Register – Windows Internet Explorer”
clip_image019
please input your details (I have put mine : ignatandrei)
clip_image020
please see that I am registered to the site
clip_image021
please verify to the database – left click on “Microsoft SQL Server Management Studio (push button)”
clip_image022
please left click in “Microsoft SQL Server Management Studio”
clip_image023
please press [F5] in “Microsoft SQL Server Management Studio”
clip_image024
please see the results – application name
clip_image025
please left click on “Execute (push button)” in “Microsoft SQL Server Management Studio” for the query that lists users.
clip_image026

Error intercepting in MVC when saving data

I have seen many times in MVC the following code , when doing a post and saving data :

try
{
        //code to save to database
}
catch
{
         ModelState.AddRuleViolations(TheModel.GetRuleViolations());
}

Why is not good ?
I will do a simple example : if some error occurs on the database level ( such as simple unique index on a name column ) or even some error occurs whenestablishing the database connection – you will never see what’s happening. And you will have no chance to repair the problem
A slighty better code is :

try
{
     ModelState.AddRuleViolations(TheModel.GetRuleViolations());
     if(ModelState.Count == 0)// no error in the model
     {
     //code to save to database
     }
}
catch(Exception ex)
{
      ModelState.AddModelError("", ex.Message);
}

Please see also how to do logging to the exception at
http://msprogrammer.serviciipeweb.ro/2010/04/19/logging-and-instrumentation/

Five common mistakes for ASP.NET (MVC) accesing resources : css, js, images, ajax

To have once for all the link to show to people, because too much makes the same error again and again.

(From here – you can use ResolveUrl or Url.Content – it’s the same for me. I use ResolveUrl because I used first …)

Case 1 The image does not display

Please check you have the following :

<img src='<%= ResolveUrl("~/Content/images/YOURIMAGE.jpg" )%>'  alt="image" style="border:0" />

Case 2 The css does not show

Please check you have the following :

<%= string.Format("<link href='{0}' type='text/css' rel='stylesheet' />", ResolveUrl("~/Content/your.css")) %>

or

<style type="text/css">
@import '<%= ResolveUrl("~/Content/your.css") %>';

</style>

Case 3 The js does not execute (undefined error)

Please check you have the following :

<script type="text/javascript" src='<%= ResolveUrl("~/Scripts/yourjs.js")%>'></script>

Case 4 The js does execute in aspx page, but did not execute under js file

Please check you DO NOT have the following

<%

in the js file. The js file is not interpreted by the same engine as aspx, so can not interpret asp.net tags. Instead , add a parameter to your function : the path.

Simple example : Let’s say in aspx you have :

<script type=”text/javascript”>

function Generate(){

window.open(‘<% Url.Action(“About”)%>’);
}

</script>

and you call Generate();

When you put in .js file, please put this :

function Generate(url){

window.open(url);
}

and call like this :

Generate('<% Url.Action(“About”)%>');

Case 5 The ajax request gives you a 404 error.

Please ensure that you have

<%= ResolveUrl("~/path”)%>

and not

/path

Bonus 1: T4MVC , http://mvccontrib.codeplex.com/releases

Bonus 2: Edit the project file and put <MvcBuildViews>true</MvcBuildViews>

( short url: http://bit.ly/asp5Mistakes)

My standard way to work with dropdown box in ASP.NET MVC – 2 steps (or 3 if you want to add long description)

I have written a small post about dropdownlist template in ASP.NET MVC here : http://msprogrammer.serviciipeweb.ro/2010/05/30/mvc-helper-templates/

I think that the dropdownlist should be explained more – aand the example will be :

First, let’s say we have Employee and Department. And we have Employee that has a field, named IDDepartment.

When edit/create a user we want to display a dropdownlist for Department in order for the user to choose the department.

Step 1 : obtain from database a list of departments and transform into List<KeyValuePair<string,string>>  – where the first string is DepartmentID and the second is Department Name.

Let’s say there is a method to do that : deptList.Values

Step 2 : display into the aspx/ascx file with List_KVP template

<%: Html.EditorFor(model => model.deptList.Values, “List_KVP”, “IDDepartment”, new { SelectedValue = Model.emp.IDDepartment })%>

Here is the weak part: the “IDDepartment” is not strongly typed. You can transform that …but it requires writing another extension. However, when you modify the code for

SelectedValue = Model.emp.IDDepartment

it is right nearby…

For reference, here is the List_KVP.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<System.Collections.Generic.KeyValuePair<string,string>>>" %>
<%
    string Val = "",style="";
    if(ViewData.Values != null && ViewData.Values.Count > 0)
    {
        Val = (ViewData["SelectedValue"]??"").ToString();
        style = (ViewData["Style"] ?? "").ToString();
    }
    if (style.Length > 0)
    {

    }
    var id=ViewData.TemplateInfo.GetFullHtmlFieldId("") ;
    id = id + "";
    %>

<select id="<%:id %>" name="<%:ViewData.TemplateInfo.GetFullHtmlFieldName("") %>" style="<%: style %>">
    <% foreach (var val in Model)
       { %>
            <option  value='<%: val.Key %>' <%:(val.Key == Val)?"selected=selected":"" %>><%: val.Value %></option>
    <%} %>
</select>
<% if(Model.Exists(x=>x.Key ==Val) )
{
       %>
       <script type="text/javascript">
   $(document).ready(function () {

       $('#<%:id  %>').change();
   }     );
      </script>
      <%
}
    %>

Oh, and if you ask how to add a description , nothing more simple :
Step1 : add to dropdown an onchange event : onchange=’javascript:funcDepartmentRetrieve(this)”
Step 2: create a java script function that retrieves the long description from the id

 <script type="text/javascript">
         <%: Html.JavaScriptFind( Model.deptList.LongDescriptionValues, "funcDepartmentLong","notfoundDepartment") %>
        </script>
  

Step 3 : Mix the 2 javascript functions

For your reference, the code for JavaScriptFind is

 public static MvcHtmlString JavaScriptFind(this HtmlHelper hh, ICollection<KeyValuePair<string,string>> values, string Name, string NotFound)
        {
            string s = "function " + Name + "(value){  switch(value){";
            string ret = "case '{0}' : return '{1}';" + Environment.NewLine;
            foreach (var item in values)
            {
                //TODO : compensate for '
                s += string.Format(ret, item.Key, item.Value);
            };
            s += string.Format("default : return '{0}' ;//+ value;"  + Environment.NewLine, NotFound);
            s += "};";//switch
            s += "}";//function
            return MvcHtmlString.Create(s);
        }

ASP.NET MVC pass data from a view to master

One of recurring questions in MVC is how to share data between views and master. The question must be reformulated : how to share data between ACTION and master.

The short answer is : Model of the View returned from Action have to put some data to the Model of the Master

The long answer here in 4 steps

Step 1: ensuring error.aspx page works fine

a)copy \Views\Shared\Site.Master into siteerror.master( the error.aspx inherits from a specialized model)

b) change in \Views\Shared\Error.aspx

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>

to

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/SiteError.Master" Inherits="System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>" %>

Step 2 : make the master strongly typed :

add a ModelMaster class

public class ModelMaster
{
public ModelMaster()
{
DataFromAction = "default data";
}
public string DataFromAction { get; set; }
}
 

and display this data to the html of \Views\Shared\Site.Master

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<MasterDataFromAction.Models.ModelMaster>" %>

//code

<h1>This is data shared from View : <%= Model.DataFromAction%></h1>

Step 3. Make the action return a strongly typed view. We will make , for example, the Index action from Home controller.

Add the ViewModelIndex class

public class ViewModelIndex :ModelMaster
{
public ViewModelIndex()
: base()
{
base.DataFromAction = "data from index";
}
}

and modify controller action and view

First Index action in \Controllers\HomeController.cs

public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
ViewModelIndex vmi = new ViewModelIndex();
vmi.DataFromAction = "here comes data from index action";
return View(vmi);
}
 

Then the view :

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MasterDataFromAction.Models.ViewModelIndex>" %>

The result is here :

When do you do this things? The sooner, the better 😉

Please find attached the project

ASP.NET MVC editing fast a property

There are some moments when you want to fast edit a property ( like a status or a name) and you do not want to load the entire “Edit” form for this.More, you are in an edit formula and do not want to add a form.

So here is the solution in ASP.NET MVC  with jquery-1.4.2.min , jquery-ui-1.8.1.custom.min for a modal dialog and some controllers. The most difficult were the javascript, so I let to the final.

We will be editing the name for an employee in 4 steps.The most difficult to understand is the step 4(javascript) , however, it can be re-used with any other object- so you can put it in a common js file.

Step 1

create a view for editing the name , named FastEditEmp


<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<BusinessLogic.Employee>" %>
<!-- edit inline the name -->
<%= Html.HiddenFor(Model=>Model.ID) %>
<%= Html.LabelFor(model=>model.Name) %>
<%= Html.EditorFor(model=>model.Name) %>

Step 2

Create the controllers and verify it works – at least the get  action .


public ActionResult FastEditEmp(int id)
 {
 var emp=EmployeeList.LoadFromID(id);
 return View(emp);
 }
 [HttpPost]
 public ActionResult FastEditEmp(BusinessLogic.Employee emp)
 {
 try
 {

 var dbid = EmployeeList.LoadFromID(emp.ID);
 UpdateModel(dbid);
 dbid.Save();
 return new JsonResult() { Data = new { ok = true, message = "succes" } };

 }
 catch (Exception ex)
 {
 //TODO : log
 return new JsonResult() { Data = new { ok = false, message = "error : " + ex.Message } };

 }
 }

Step 3
Create the hyperlink to edit the name of an employee (emp) :

<a href=’javascript:EditModal(<%= emp.ID%>,”<%=Url.Action(“FastEditEmp”,new {id=emp.ID}) %>”)’>Edit name</a>

Step 4

This is the most difficult one, the javascript. However, more of the code is common no matter what you want to edit fast modal :


var wait = '<%= ResolveUrl("~/Content/Images/wait.gif") %>';
function EditModal(empID,url)
{
// load a please wait dialog
 var $dialog1 = $('<div></div>')
 .html("Loading data <br /> <img src='" + wait + "'></img>")
 .dialog({ autoOpen: false,
 title: 'please wait'
 });
 $dialog1.dialog('open');
// so the please wait dialog was shown to the user. Now load the content of the action specified in url
 $.get(url,
 {
 ID: empID
 },
 function(txt) {

 var $dialog = $('#dialog');
 $dialog.html('');
 $dialog.html(txt)
 .dialog({
 autoOpen: false,
 title: 'Edit',
 modal: true,
 show: 'blind',
 hide: 'explode',
 closeOnEscape: false,
 buttons: {

 "Close": function() {
//just cleanup - no saving
 var allInputs = $(":input:not(:button)");
 var ser = allInputs.serialize();
 allInputs.remove();
 allInputs.empty();
 $(this).dialog("close");
 $(this).dialog('destroy');

 },
 "Save": function() {
//now saving data : serializing and posting
 var allInputs = $(":input:not(:button)");
 var ser = allInputs.serialize();
 allInputs.remove();
 allInputs.empty();

 window.alert(ser); -- debug mode
 $(this).dialog('close');
 $(this).dialog('destroy');
//saving data by posting to same url!
 $.post(url,
 ser,
 function(text) {
 $(this).dialog("close");
 $(this).dialog('destroy');
 if (text.ok) {

 window.alert("Saved - you can change here the display id with jquery");
 window.location.reload(true);
 }
 else {
 window.alert(text.message);
 }

 }
 );

 }

 }

 });

 $dialog1.dialog('close');//closing the wait dialog
 $dialog.dialog('open'); // show main editing dialog
 });

}

 </script>

As always, please find here the saving modal files. Please execute first the emp.sql file, then modify the connection string into the web.config .

Asp.NET MVC and DOS – re-using the ViewModels

(Please read first : http://msprogrammer.serviciipeweb.ro/2010/03/29/asp-net-mvc-orm-and-viewmodels/ )

One of the biggest challenges in programming was write once- GUI everywhere ( Ok, ORM impedance mismatch is another story)

I mean by that re-using the logic from an application in another application. ASP.NET MVC , with the commitment to strongly viewmodels, make me think that it will be now easier to transfer the viewmodels to an console application.

Let’s see the usual Employee-Department and creation.

First the Database :

Then the ViewModel for creating an employeeand for list of employees


public class ViewModelEmployee
 {
 public static DepartmentList ListOfDepartments
 {
 get
 {
 //TODO : put this into cache to not load every time
 DepartmentList dl = new DepartmentList();
 dl.Load();
 return dl;
 }
 }
 }
 public class ViewModelEmployeeCreate : ViewModelEmployee
 {
 public Employee emp = new Employee();

 public static void SaveNew(Employee emp)
 {
 emp.SaveNew();
 }
 }
 public class ViewModelEmployeeList : ViewModelEmployee
 {
 public EmployeeList employees;
 public void Load()
 {
 employees = new EmployeeList();
 employees.Load();
 }
 }

And now the magic :

ASP.NET MVC DOS

[HttpPost]
 public ActionResult Create(Employee emp,long DepartmentID)
 {
 try
 {

 emp.Department = new Department() { ID = DepartmentID };
 ViewModelEmployeeCreate.SaveNew(emp);

 return RedirectToAction("Index");
 }
 catch(Exception ex)
 {
 ModelState.AddModelError("", ex.Message);
 return View(new ViewModelEmployeeCreate(){emp=emp});
 }
 }


Console.WriteLine("choose department");
 var listdep=ViewModelEmployee.ListOfDepartments;
 foreach (var item in listdep)
 {
 Console.WriteLine(item.ID + ")" + item.Name);

 }
 string s=Console.ReadLine();
 long DepartmentID;
 if (!long.TryParse(s, out DepartmentID))
 {
 Console.WriteLine("exit : not a long :" + s);
 return;
 }
 if (!listdep.Exists(item => item.ID == DepartmentID))
 {
 Console.WriteLine("not a valid id:" + s);
 return;
 }
 Employee emp = new Employee();
 emp.Department = new Department() { ID = DepartmentID };
 Console.Write("employee name ?");
 emp.Name= Console.ReadLine();
 ViewModelEmployeeCreate.SaveNew(emp);

Now for listing employees:

ASP.NET MVC DOS
public ActionResult Index()
        {
            ViewModelEmployeeList vmel = new ViewModelEmployeeList();
            vmel.Load();
            return View(vmel);
        }

  ViewModelEmployeeList vmel = new ViewModelEmployeeList();
            vmel.Load();
            foreach (var item in vmel.employees)
            {
                Console.WriteLine(item.Name);
            }

As you can see , the codes are really similar ( although the console application is filled with first verification and the MVC is not )

Please download the application from testaddropdownlist

To install : run the emp.sql file, change in the app.config/web.config the connection string to point to the real database/sql server.

Summary : This simple application shows how to re-use ViewModels from an ASP.NET MVC and a DOS Console Application

Homework : Add  WindowsForms application and do the same.

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.