Visualizzare e modificare dati usando Entity Framework

Questo articolo mostra un esempio di pagine Master-Details usando Entity Framework: la pagina master contiene una griglia con l'elenco dei prodotti, e cliccando su un prodotto si viene proiettati nella pagina di dettaglio dove è possibile visualizzare i dati completi e modificare il prodotto. E' implementata anche la funzione di inserimento prodotto.

Per l'elenco ho usato un GridView associato a un EntityDataSource.
Per la pagina di dettaglio invece anziché usare un controllo DetailsView ho creato manualmente un form e usato codice VB/Linq to Entities per l'inserimento e la modifica dei dati. Questo permette di avere maggiore controllo sulla pagina.

Pagina Master: elenco prodotti

Creare un Gridview e un pulsante per inserire un nuovo prodotto.

Aggiungere e configurare un Entity DataSource.

Nel DataSource modificare i campi chiave esterna, ad esempio:

Select="it.[ProductID], it.[ProductName], it.[CategoryID]"

diventa:

Select="it.[ProductID], it.[ProductName], it.[Category].[CategoryName]"

Così facendo viene estratto il nome della categoria e non l'id che è poco significativo per l'utente finale.

Configurare in questo modo il GridView:

  • Cliccare su Refresh Schema dallo Smat Task menu per allinearlo alle modifiche effettuate sul DataSource
  • Modificare gli HeaderText per avere intestazioni descrittive al posto dei nomi dei campi della tabella
  • Se necessario attivare l'ordinamento e la paginazione dalle apposite checkbox
    nota: la paginazione richiede che venga specificata la proprietà OrderBy
  • Aggiungere un TemplateField al GridView con il link che porta alla pagina di dettaglio prodotto.
    Si richiama la pagina di dettaglio passando in query string l'id del prodotto da modificare
  • Impostare Skin e stili Css per migliorare l'aspetto della griglia

Gestire l'evento click del pulsante per l'inserimento prodotto: semplicemente fa un redirect alla pagina di dettaglio senza passare alcun parametro in query string.

Protected Sub btnNuovoProdotto_Click(sender As Object, e As System.EventArgs)
		Handles btnNuovoProdotto.Click
	Response.Redirect("DettaglioProdotto.aspx")
End Sub

Il codice che si ottiene è simile a questo:

<form id="form1" runat="server">
<div>
	<asp:GridView ID="GridView1" runat="server" AllowSorting="True" AutoGenerateColumns="False"
			DataSourceID="edsProdotti" AllowPaging="True" PageSize="20">
		<Columns>
			<asp:BoundField DataField="ProductID" HeaderText="ID" ReadOnly="True" SortExpression="ProductID" />
			<asp:BoundField DataField="ProductName" HeaderText="Name" ReadOnly="True" SortExpression="ProductName" />
			<asp:BoundField DataField="CategoryName" HeaderText="Category" ReadOnly="True" SortExpression="CategoryName" />				
			<asp:TemplateField>
				<ItemTemplate>
					<a href="DettaglioProdotto.aspx?prod=<%# Eval("ProductID") %>">Dettagli</a>						
				</ItemTemplate>
			</asp:TemplateField>
		</Columns>
	</asp:GridView>
	
	<asp:Button ID="btnNuovoProdotto" runat="server" Text="Nuovo Prodotto" />
	
	<asp:EntityDataSource ID="edsProdotti" runat="server" ConnectionString="name=NorthwindEntities"
			DefaultContainerName="NorthwindEntities" EnableFlattening="False" 
			EntitySetName="Products" EntityTypeFilter="Product" 
			Select="it.[ProductID], it.[ProductName], it.[Category].[CategoryName]"
			OrderBy=" it.[ProductName]"
			StoreOriginalValuesInViewState="False">
	</asp:EntityDataSource>
</div>
</form>

Pagina Details: dettaglio prodotto

Creare un form html contenente le etichette e i campi TextBox, DropDownList ecc.

Viene usato lo stesso form sia per l'inserimento che per la modifica.

Aggiungere un EntityDataSource per leggere le categorie e associarlo alla DropDownList delle categorie.

Aggiungere un EntityDataSource per leggere i fornitori (Suppliers) e associarlo alla DropDownList dei fornitori.

<form id="form1" runat="server">
<div>
	<%-- Data Source per le categorie --%>
	<asp:EntityDataSource ID="eds_Category" runat="server" ConnectionString="name=NorthwindEntities" 
		DefaultContainerName="NorthwindEntities" EnableFlattening="False" EntitySetName="Categories" 
		Select="it.[CategoryID], it.[CategoryName]">
	</asp:EntityDataSource>
	
	<%-- Data Source per i fornitori --%>
	<asp:EntityDataSource ID="eds_Supplier" runat="server" ConnectionString="name=NorthwindEntities"
		DefaultContainerName="NorthwindEntities" EnableFlattening="False" EntitySetName="Suppliers" 
		Select="it.[SupplierID], it.[CompanyName]">
	</asp:EntityDataSource>
	
	<%-- Form inserimento/modifica prodotto --%>
	<table>
		<tr>
			<td>Name</td>
			<td><asp:TextBox ID="txtName" runat="server"></asp:TextBox></td>
		</tr>
		<tr>
			<td>Category</td>
			<td>
				<asp:DropDownList ID="ddlCategory" runat="server" DataSourceID="eds_Category" 
					DataTextField="CategoryName" DataValueField="CategoryID">
				</asp:DropDownList>
			</td>
		</tr>
		<tr>
			<td>Supplier</td>
			<td>
				<asp:DropDownList ID="ddlSupplier" runat="server" DataSourceID="eds_Supplier"
					DataTextField="CompanyName" DataValueField="SupplierID">
				</asp:DropDownList>
			</td>
		</tr>
		<tr>
			<td>Quantity Per Unit</td>
			<td><asp:TextBox ID="txtQuantityPerUnit" runat="server"></asp:TextBox></td>
		</tr>
		<tr>
			<td>Unit Price</td>
			<td><asp:TextBox ID="txtUnitPrice" runat="server" /></td>
		</tr>
		<tr>
			<td>Units In Stock</td>
			<td><asp:TextBox ID="txtUnitsInStock" runat="server"></asp:TextBox></td>
		</tr>
		<tr>
			<td>
				Units On order</td>
			<td><asp:TextBox ID="txtUnitsOnOrder" runat="server" /></td>
		</tr>
		<tr>
			<td>Reorder Level</td>
			<td><asp:TextBox ID="txtReorderLevel" runat="server" /></td>
		</tr>
		<tr>
			<td>Discontinued</td>
			<td><asp:CheckBox ID="chkDiscontinued" runat="server" /></td>
		</tr>
		<tr>
			<td colspan="2" align="center"><asp:Button ID="btnSave" runat="server" Text="Save" /></td>
		</tr>
	</table>

</div>
</form>

Nel Page_Load scrivere il codice per leggere l'id del prodotto in QueryString e capire se si è in modalità inserimento o modifica. Nel secondo caso si caricano i dettagli del prodotto dall'Entity Model (quindi dal db) e si valorizzano i vari campi.

Nell'evento Click del pulsante si scrive il codice per salvare i dati.

Imports NorthwindModel

Partial Class DettaglioProdotto
    Inherits System.Web.UI.Page

	Dim _id As Integer = -1

	Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load

		'Verifico se alla pagina è stato trasmesso il parametro "id" in query string
		If Not String.IsNullOrEmpty(Request.QueryString.Get("prod")) Then
			_id = CType(Request.QueryString.Get("prod"), Integer)
		End If

		If Not Page.IsPostBack And _id > -1 Then

			'Se è stato trasmesso un id prodotto allora siamo in modalità modifica
			'	pertanto valorizzo i vari controlli
			Using myEntities As New NorthwindEntities()
				Dim myProduct = (
				 From p In myEntities.Products
				 Where p.ProductID = _id
				 Select p).SingleOrDefault()

				If myProduct IsNot Nothing Then
					txtName.Text = myProduct.ProductName

					'imposto il valore selezionato nelle drop down list
					ddlCategory.SelectedValue = myProduct.CategoryID
					ddlSupplier.SelectedValue = myProduct.SupplierID

					txtQuantityPerUnit.Text = myProduct.QuantityPerUnit
					txtUnitPrice.Text = myProduct.UnitPrice
					txtUnitsInStock.Text = myProduct.UnitsInStock
					txtUnitsOnOrder.Text = myProduct.UnitsOnOrder
					txtReorderLevel.Text = myProduct.ReorderLevel

					'checkbox
					chkDiscontinued.Checked = myProduct.Discontinued
				End If
			End Using

		End If

	End Sub

	Protected Sub btnSave_Click(sender As Object, e As System.EventArgs) Handles btnSave.Click

		Using myEntities As New NorthwindEntities
			Dim myProduct As Product

			If _id = -1 Then
				'Inserimento
				myProduct = New Product()
				myEntities.AddToProducts(myProduct)
			Else
				'Modifica
				myProduct = (
				 From p In myEntities.Products
				 Where p.ProductID = _id
				 Select p).SingleOrDefault()
			End If

			With myProduct
				.ProductName = txtName.Text
				.CategoryID = ddlCategory.SelectedValue
				.SupplierID = ddlSupplier.SelectedValue
				.QuantityPerUnit = txtQuantityPerUnit.Text
				.UnitPrice = txtUnitPrice.Text
				.UnitsInStock = txtUnitsInStock.Text
				.UnitsOnOrder = txtUnitsOnOrder.Text
				.ReorderLevel = txtReorderLevel.Text
				.Discontinued = chkDiscontinued.Checked
			End With

			'ToDo:
			'1. validazione input utente
			'2. verificare se il prodotto nel frattempo è già stato modificato da un altro utente
			'3. getire eventuali errori

			myEntities.SaveChanges()
			Response.Redirect("Prodotti.aspx")
		End Using

	End Sub
End Class

ToDo:

Questi sono possibili miglioramenti:

  • Usare stili Css per rendere l'aspetto delle pagine più gradevole e leggibile
  • Validare l'input dell'utente
  • Prima di salvare i dati assicurarsi che nel frattempo il record non sia stato eliminato oppure modificato da un altro utente
  • In alternativa si può attivare un semaforo che impedisca di modificare i dati di un elemento qualora un altro utente stia operando su di essi.

Autore: Sergio Roberto Boarina