Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
1
Part 1. An Experience Using LINQ to SQL
Exploring the LINQ Entity-Model
What is LINQ ? 1[1]
Language Integrated Query (or LINQ, pronounced “link”), is a set of Microsoft .NET technologies that provide built-in
language querying functionality similar to SQL, not only for database access, but for accessing data from any source.
The current LINQ family of technologies and concepts allows an extensible set of operators that work over objects,
SQL data and XML data sources. It is expected that LINQ will expand in the future to a larger number of data
domains.
A gentle Introduction to LINQ [2]
Let us consider the following C# code fragment
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConLinq1 { class Program { static void Main() { string[] myNames = { "Burke", "Connor", "Frank", "Bingo", "Bango", "Bongo", "Everett", "Albert", "George", "Becky", "Harris", "David" }; // make a list of names made of 5 characters var expr = from s in myNames where ((s.Length == 5) && (s.StartsWith("B"))) orderby s select s.ToUpper(); // show results foreach (string item in expr) Console.WriteLine(item); // show actual type of anonymous variable Console.WriteLine("expr type:\n" + expr.GetType()); Console.WriteLine("\nDone..."); Console.ReadLine(); } }//class }
1 [1] Taken from http://msdn.microsoft.com/en-us/library/bb386976.aspx [2] The LINQ Project .NET Language Integrated Query. Don Box and Anders Hejlsberg. Microsoft Corporation. May 2006. Available at http://download.microsoft.com/download/5/8/6/5868081c-68aa-40de-9a45-a3803d8134b8/LINQ_Project_Overview.doc
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
2
Output
BANGO BECKY BINGO BONGO BURKE expr type: System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.String,System.String] Done...
Here var expr defines an anonymous variable resulting from evaluating a LINQ query expression. A query expression
operates on one or more information sources by applying one or more query operators from either the standard set
of query operators or domain-specific operators. This expression uses three of the standard query operators: Where,
OrderBy, and Select. Observe the Where clause in this C# program is phrased using C# syntax (instead of mixing host
language and SQL syntax for instance).
The statements above are semantically identical to the following explicit syntax shown in C#:
var expr = myNames .Where ( (s => (s.Length == 5) && (s.StartsWith("B")) ) ) .OrderBy (s => s) .Select (s => s.ToUpper());
The arguments to the Where, OrderBy, and Select operators are called lambda expressions, which are fragments of
code much like delegates. They allow the standard query operators to be defined individually as methods and strung
together using dot notation.
Many query operators allow the user to provide a function that performs filtering, projection, or key extraction. The
query facilities build on the concept of lambda expressions, which provides developers with a convenient way to write
functions that can be passed as arguments for subsequent evaluation. Lambda expressions are similar to CLR
delegates and must adhere to a method signature defined by a delegate type. To illustrate this, we can expand the
statement above into an equivalent but more explicit form using the Func delegate type:
Func<string, bool> filter = s => (s.Length == 5) && (s.StartsWith("B")); Func<string, string> extract = s => s; Func<string, string> project = s => s.ToUpper(); var expr = names.Where(filter) .OrderBy(extract) .Select(project);
Anonymous types allow data types to encapsulate (on-the-fly) a set of properties (fields) into a single object without
having to first explicitly define a type. This is an important feature for the SQL-like LINQ feature that is integrated into
C# and VB.NET. Since anonymous types do not have a named typing, they must be stored in variables declared using
the var keyword, telling the C# compiler to use type inference for the variable. The properties created are read-only in
C#, however they are read-write in VB.net. In our example the type of the resulting expression is dynamically
determined after the query evaluates and results into
System.Linq.Enumerable+WhereSelectEnumerableIterator`2[System.String,System.String]
The output type is inferred as System.String.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
3
Another example is:
var person = new { FirstName = "John", LastName = "Smith", Salary = 75000.99 };
here the type of the variable person is the combination of two strings and a double, more specifically
<>f__AnonymousType0`3[System.String,System.String,System.Double]
LINQ Operators
LINQ to Objects2 extends any type that inherits from IEnumerable (which is almost every collection class in .NET, from
simple Arrays to List<T>) to support query operations similar to those available in SQL. We can write queries using
any of the built-in Standard Query Operators, or add our own operators if we need to. The standard operators cover a
wide variety of categories, at present there are over fifty that form the backbone of LINQ.
Operator Type Operator Name
Aggregation Aggregate, Average, Count, LongCount, Max, Min, Sum
Conversion Cast, OfType, ToArray, ToDictionary, ToList, ToLookup, ToSequence
Element DefaultIfEmpty, ElementAt, ElementAtOrDefault, First, FirstOrDefault,
Last, LastOrDefault, Single, SingleOrDefault
Equality EqualAll
Generation Empty, Range, Repeat
Grouping GroupBy
Joining GroupJoin, Join
Ordering OrderBy, ThenBy, OrderByDescending, ThenByDescending, Reverse
Partitioning Skip, SkipWhile, Take, TakeWhile
Quantifiers All, Any, Contains
Restriction Where
Selection Select, SelectMany
Set Concat, Distinct, Except, Intersect, Union
2 Retrieved 4/18/2010 http://msdn.microsoft.com/en-us/library/bb308959(v=MSDN.10).aspx
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
4
What is LINQ to SQL?
LINQ to SQL is an (O/RM) Object Relational Mapping component of the .NET Framework that provides a run-time
infrastructure for managing relational data as objects. In LINQ to SQL, the data model of a relational database is
mapped to an object model expressed in the programming language of the developer. When the application runs,
LINQ to SQL translates into SQL the language-integrated queries in the object model and sends them to the database
for execution. When the database returns the results, LINQ to SQL translates them back to objects that you can work
with in your own programming language.
You can query the database using LINQ, as well as update/insert/delete data from it. LINQ to SQL fully supports
transactions, views, and stored procedures. It also provides an easy way to integrate data validation and business
logic rules into your data model.
Object-Design Surface
The Object Relational Designer (O/R Designer) provides a visual design surface for creating LINQ to SQL entity classes
and associations (relationships) that are based on objects in a database. In other words, the O/R Designer is used to
create an object model in an application that maps to objects in a database. It also generates a strongly-typed
DataContext that is used to send and receive data between the entity classes and the database. The O/R Designer
also provides functionality to map stored procedures and functions to DataContext methods for returning data and
populating entity classes. Finally, the O/R Designer provides the ability to design inheritance relationships between
entity classes.
Figure 3 shows a fragment of the COMPANY database rendered as a Class-Diagram. It includes three entity classes:
EMPLOYEE, WORKS_ON, and PROJEC. The attributes (also called properties or class variables) of each class map to the
columns of a corresponding table in the relational database. Each instance of a class entity represents a row within
the database table.
The arrows between the three entity classes represent associations/relationships between the different entities. These
are typically modeled using primary-key/foreign-key relationships in the database. The direction of the arrows on the
design-surface indicates whether the association is a one-to-one or one-to-many relationship. Strongly-typed
properties will be added to the entity classes based on this fact. For example, the Employee class has a one-to-many
relationship with the Works_On class, representing the fact an employee could have several assignments. This means
it will have a "WORK_ONs" property which is a collection of WORK_ON objects within that category. Appendix X
shows fragments of the WORKS_ON definition produced by the O/R Designer.
The right-hand method pane within the LINQ to SQL design surface (Figures 2 and 3) contains a list of stored
procedures that interact with our database model. For example methods such as “Hire_Employee”, “Add_Project”,
“Remove_Employee_From_Project” can be saved in the database to be invoked by our client applications.
Understanding the DataContext Class
Visual Studio persists .NET classes that represent the entities and database relationships that we have modeled. For
each LINQ to SQL designer file added to our solution, a custom DataContext class will also be generated. This
DataContext class is the main conduit by which we'll query entities from the database as well as apply changes. The
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
5
DataContext class created will have properties that represent each Table we modeled within the database, as well as
methods for each Stored Procedure we added.
It tracks changes that you made to all retrieved entities and maintains an "identity cache" that guarantees that entities
retrieved more than one time are represented by using the same object instance. In general, a DataContext instance is
designed to last for one "unit of work" however your application defines that term.
The DataContext class contains the connection string information and the methods for connecting to a database and
manipulating the data in the database. By default, the DataContext class contains several methods that you can
call, such as the SubmitChanges method that sends updated data from LINQ to SQL classes to the database. You can
also create additional DataContext methods that map to stored procedures and functions. In other words, calling
these custom methods will run the stored procedure or function in the database that the DataContext method is
mapped to. You can add new methods to the DataContext class just as you would add methods to extend any class.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
6
Lab Experience
We assume you have installed a copy of the COMPANY database in your SQL-Server (for details about the datasets
consult Elmasri-Navathe textbook).
1. Create a new C# project. Call it Linq2Sql01
2. Open the Server Explorer and create a new Data Connection to the MS-SQL COMPANY database (see MS-SQL
Database Diagram in Appendix A).
3. You must add to the C# project a Linq class. Follow the steps:
(Main Menu) Project | Add New Item… | Linq to Sql class. Name it: CompanyLinq.dbml. Click OK
Figure 1. Adding a Linq to Sql class
Figure2. The Object/Relational Designer Surface
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
7
4. Click on the Server Explorer. Open the Tables folder. Drag the EMPLOYEE, WORKS_ON, and PROJECT tables to the
Object-Relational Designer panel (see figure above). This diagram depicts the collection of entities and their defined
relationships (other components could be added later). Those elements are accessible from our application.
Figure 3. A Class-Diagram showing three entities (Employee, Works_On, Project) and their relationships.
5. Enter the following code:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Linq2Sql01 { class Program { static void Main(string[ ] args) { //demo1. Retrieve male employees CompanyLinqDataContext myContext = new CompanyLinqDataContext(); var query1 = from e in myContext.EMPLOYEEs where e.sex.Equals("M") select e; foreach (var e in query1) { Console.WriteLine("Name: {0} {1} \tDno: {2}", e.fname, e.lname, e.dno); } Console.WriteLine("\nDone..."); Console.ReadLine(); } } }
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
8
Output
Observations
a) The new LINQ class was called CompanyLinq. Therefore its associated DataContext class is named:
CompanyLinqDataContext
b) The clause “var query1” defines query1 as a variable of “anonymous type”. The shape of query1 is dictated by
the result of the LINQ expression. In our example the type of query1 is similar to Employee type.
c) The LINQ expression consists of three parts: from … where … select …
d) In our example the “from” portion is: from e in myContext.EMPLOYEEs. The collection EMPLOYEEs is
provided by .NET as a way of traversing the EMPLOYEE table (its naming is done by .NET)
e) The filtering of data is done through the phrase: where e.sex.Equals(“M”). The condition is written in the
form expected by the host language (in this case C#). For instance, the previous expression could also be
written as where e.sex == „M‟. Notice that Intellisense provides a list of e attributes as soon as you type “e.”
f) The select clause defines the output. In this example the entire e data type is retrieved. You may use the
expression new (e. fname, e.Lname, e.dno) to create a new entity type holding the three attributes: first
name, last name, and dept. number.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
9
Organizing the Rest of the Tutorial.
Replace the main method entered before with the following (larger) sample. You can try each of the fragments by
uncommenting/commenting the corresponding demo (there are nine altogether).
using System; using System.Collections.Generic; using System.Linq; using System.Text; // trying LINQ on the company database namespace Linq2SqlCompany01 { class Program { static void Main(string[] args) { // STEP 1 // Open the Server Explorer. Add a New Connection to the // MS-SQL Database called: Company // --------------------------------------------------------------- // STEP 2. // You must add to the project a Linq class, do this // Main Menu Project | Add New Item | Linq to Sql class // name it: CompanyLinq.dbml. Click OK // ---------------------------------------------------------------- // STEP3 // Define a Linq Data-Contex object CompanyLinqDataContext myContext = new CompanyLinqDataContext(); try { // QUERY1: Listing all MALE employees //ShowMaleEmployees(myContext); // QUERY2: List all employees and projects in which they work //ShowEmployeesProjects1(myContext); //QUERY3. Similar to Query2 but using a 'JOIN' expression //ShowEmployeesProjects2(myContext); //DEMO4: Insert a new assignment for employee 123456789 //InsertSingleAssignment(myContext); //DEMO5: Insert multiple assignment for employee 123456789 //InsertMultipleAssignments(myContext); //DEMO6: Delete one assignment for 123456789 //DeleteSingleAssignment(myContext); //DEMO7: Delete multiple assignment for 123456789 (pno >= 10) //DeleteMultipleAssignments(myContext); //DEMO8: Using STORED PROC. Simple ResultSet made of one table type //CallStoredProcSimpleCase(myContext); //DEMO9: Using STORED PROC. Complex ResultSet made of various table type //CallStoredProcComplexCase(myContext); Console.WriteLine("\nDone..."); Console.ReadLine(); } catch (Exception e) { Console.WriteLine("\n Problems..." + e.Message); Console.ReadLine(); }
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
10
}// main //-------------------------------------------------------------------------------------- public static void ShowMaleEmployees(CompanyLinqDataContext myContext) { var query1 = from e in myContext.EMPLOYEEs where e.sex == 'M' select e; foreach (EMPLOYEE emp in query1) { Console.WriteLine("{0} \t {1} {2}", emp.ssn, emp.fname, emp.lname); } }// ShowMaleEmployees // ---------------------------------------------------------------------- public static void ShowEmployeesProjects1(CompanyLinqDataContext myContext) { // for each employee list his/her projects // solution is based on (brute force) nested loops var query2e = from e in myContext.EMPLOYEEs select e; foreach (EMPLOYEE eRec in query2e) { Console.WriteLine("{0} {1}", eRec.ssn, eRec.lname); // find the assignments of the current employee var query2w = from w in myContext.WORKS_ONs where w.essn == eRec.ssn select w; foreach (WORKS_ON wRec in query2w) { // find the (single) project description of each assignment var query2p = (from p in myContext.PROJECTs where p.pnumber == wRec.pno select p).Single(); Console.WriteLine("\t{0} {1} {2}\t{3}", wRec.essn, wRec.pno, query2p.pname, wRec.hours); } } }// ShowEmployeesProjects1 // ---------------------------------------------------------------------- public static void ShowEmployeesProjects2(CompanyLinqDataContext myContext) { // for each employee list his/her projects // solution is based on (intelligent) join operations var query3 = from e in myContext.EMPLOYEEs from w in myContext.WORKS_ONs from p in myContext.PROJECTs where (e.ssn == w.essn) && (w.pno==p.pnumber) select new { e.ssn, e.lname, w.essn, w.pno, w.hours, p.pname }; foreach (var ewp in query3) { Console.WriteLine("{0} {1} Proj: {2} {3} Hours: {4} ", ewp.ssn, ewp.lname, ewp.pno, ewp.pname, ewp.hours); } }// ShowEmployeesProjects2 // ---------------------------------------------------------------------- public static void InsertSingleAssignment(CompanyLinqDataContext myContext) { // use Server Explorer. Create a Query to inspect assignments of 123456789 // (currently working on projects 1 and 2) // place 123456789 on project 3 for 1 hour WORKS_ON wRec = new WORKS_ON { essn = "123456789", pno = 3, hours = 1 }; myContext.WORKS_ONs.InsertOnSubmit(wRec); myContext.SubmitChanges();
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
11
// use Server Explorer to inspect employee's work-load } // InsertSingleAssignment // ---------------------------------------------------------------------- public static void InsertMultipleAssignments(CompanyLinqDataContext myContext) { // use Server Explorer to see entries of 123456789 in WORKS_ON // create a list of assignments for emp 123456789 List<WORKS_ON> wList = new List<WORKS_ON>(); WORKS_ON w1 = new WORKS_ON(){ essn="123456789", pno=10, hours=1}; WORKS_ON w2 = new WORKS_ON(){ essn="123456789", pno=20, hours=1}; WORKS_ON w3 = new WORKS_ON(){ essn="123456789", pno=30, hours=1}; wList.Add(w1); wList.Add(w2); wList.Add(w3); myContext.WORKS_ONs.InsertAllOnSubmit(wList); myContext.SubmitChanges(); // use Server Explorer to see additions made to 123456789's workload }//InsertMultipleAssignments // ---------------------------------------------------------------------- public static void DeleteSingleAssignment(CompanyLinqDataContext myContext) { // use Server Explorer. Create a Query to inspect assignments of 123456789 // (currently working on projects 1, 2, 3, 10, 20, 30) // removing employee from project 3; var wRec = myContext.WORKS_ONs.Single( w => (w.essn == "123456789")&&(w.pno==3)); myContext.WORKS_ONs.DeleteOnSubmit(wRec); myContext.SubmitChanges(); } // ---------------------------------------------------------------------- public static void DeleteMultipleAssignments(CompanyLinqDataContext myContext) { // removing employee 123456789 from projects 10, 20, 30 // observe the use of DeleteAllOnSubmit to get rid of the list // (instead of DeleteOnSubmit used to eliminate a single instance) var assignments = (from w in myContext.WORKS_ONs where (w.pno >= 3) && ( w.essn =="123456789") select w); myContext.WORKS_ONs.DeleteAllOnSubmit(assignments); myContext.SubmitChanges(); }// DeleteMultipleAssignments // ---------------------------------------------------------- public static void CallStoredProcSimpleCase(CompanyLinqDataContext myContext) { // Calling a StoredProc that returns rows from a single table // use Server Explorer to write the following MSSQL proc. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * ALTER PROCEDURE dbo.GetProjectsByDept ( @theDeptNo int ) AS SET NOCOUNT ON select p.* from Project p where p.dnum = @theDeptNo; RETURN * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** */ // Open the Object-Relational Designer window // Drag the icon representing in the Server Explorer the // procedure. Drop it on the right-side panel (Methods) // Right-Click on the procedure to display a pop-up window // from which we choose PROPERTIES
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
12
// On the property panel select RETURN TYPE and choose // PROJECT as the type to be made by the stored-proc // using anonymous type // var myProducts = myContext.GetProjectsByDept(5); // equivalent strongly-typed statement is IEnumerable<PROJECT> myProducts = myContext.GetProjectsByDept(5); foreach(var p in myProducts) { Console.WriteLine("{0} {1} {2}",p.pnumber, p.pname, p.dnum); } }//CallStoredProcSimpleCase // ---------------------------------------------------------- public static void CallStoredProcComplexCase(CompanyLinqDataContext myContext) { // Calling a StoredProc that returns rows from various tables // or returns a value made by a function (not a table type) // use Server Explorer to write the following MSSQL proc. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * ALTER PROCEDURE GetName2 (@aSSN int, @answer nvarchar(100) output ) as BEGIN -- Returns salutation + full-name of given employee DECLARE @gender nvarchar(2) set @answer = ' ' select @gender = sex from employee where ssn = @aSSN; if @@rowcount = 0 RETURN @answer; select @answer = (fname + ' ' + lname) from employee where ssn = @aSSN; if @gender = 'F' set @answer = 'Ms. ' + @answer else set @answer = 'Mr. ' + @answer; RETURN 0 END; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** */ // Open the Object-Relational Designer window // Drag the icon representing the proc from the Server // Explorer. Drop it on the right-side panel (Methods) // Calling a stored Function to get emp's full name String fullName = ""; var myResult = myContext.GetName2(123456789, ref fullName); Console.WriteLine("{0} ", fullName ); // Calling a stored proc returning rows from various tables // use Server Explorer to enter the folowing MSSQL procedure // test it. Drag to Object-Relational Designer right panel. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ALTER PROCEDURE dbo.GetEmpNameProjectName ( @theDno int ) AS select e.ssn, e.lname, p.pname, w.hours from employee e, project p, works_on w where e.ssn = w.essn and w.pno = p.pnumber and e.dno = @theDno; RETURN * * * * * * * * * * * *** * * * * * * * * * * * * * * * * * */ Console.WriteLine("\nEmployees in Dept 5"); var myMixedResults = myContext.GetEmpNameProjectName(5); foreach (var ep in myMixedResults) { Console.WriteLine("{0} {1} {2} {3}", ep.ssn, ep.lname, ep.pname, ep.hours);
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
13
} }//CallStoredProcComplexCase }// program }
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
14
Output
123456789 John Smith
222333444 Inigo Montoya
333445555 Franklin Wong
666884444 Ramesh Narayan
888665555 James Borg
987987987 Ahmad Jabbar
123456789 Smith
123456789 1 32.5 ProductX
123456789 2 7.5 ProductY
222333444 Montoya
333445555 Wong
333445555 1 1.0 ProductX
333445555 2 10.0 ProductY
333445555 10 10.0 Computerization
333445555 20 10.0 REORGANIZATION
453453453 English
453453453 1 20.0 ProductX
453453453 2 20.0 ProductY
666884444 Narayan
666884444 3 40.0 PRODUCTZ
888665555 Borg
888665555 20 0.0 REORGANIZATION
987654321 Wallace
987654321 20 15.0 REORGANIZATION
987654321 30 20.0 Newbenefits
987987987 Jabbar
987987987 10 35.0 Computerization
987987987 30 5.0 Newbenefits
999887777 Zelaya
999887777 10 10.0 Computerization
999887777 30 30.0 Newbenefits
123456789 Smith Proj: 1 ProductX Hours: 32.5
123456789 Smith Proj: 2 ProductY Hours: 7.5
333445555 Wong Proj: 1 ProductX Hours: 1.0
333445555 Wong Proj: 2 ProductY Hours: 10.0
453453453 English Proj: 1 ProductX Hours: 20.0
453453453 English Proj: 2 ProductY Hours: 20.0
666884444 Narayan Proj: 3 PRODUCTZ Hours: 40.0
Done...
Query1
Query2
Query3
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
15
After INSERT operations (Using Server Explorer | New Query)
After DELETE records
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
16
Using Stored-Procedures
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
17
PART 2. Web Apps Using LINQ-to-SQL
Exploring the LinqDataSource Class
1. Create an ASP.NET Web Application (WebLinq2Sql1).
2. Right-click on the application. Add | New Item… | Linq to Sql Classes. Name it: COMPANY.dbml
3. Click on Server Explorer. Drag-drop on Object-Relational-Designer tables: EMPLOYEE, WORKS_ON,
PROJECT. Save the design.
4. Use Solution Explorer. Click on the DEFAULT.ASP (Design) page. Click on ToolBox > Data. Drag-Drop a
GridView on the ASP form.
5. Select the code-behind page of the Default.asp page. Modify the Page_Load method as follows:
protecetd void Page_Load(object sender, EventArgs e) { COMPANYDataContext myContext = new COMPANYDataContext(); var query1 = from emp in myContext.EMPLOYEEs where ((emp.sex == 'F') || (emp.dno == 5)) select emp; GridView1.DataSource = query1; GridView1.DataBind(); }
6. Execute the application. It will retrieve a list of employees who either are ladies or work for dept. no. 5.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
18
Version 2. A custom list of attributes – Using the new {…} clause
You may customize the output list produced by the LINQ expression and consequently adjust the GridView‟s
datasource. For instance, we may request the LINQ expression to project only the employee‟s SSN, first and last name,
and dept no. Similarly you may modify the aesthetics of this app by clicking on the gridView‟s menu button (top-
right) and selecting “Auto Format…”. Change to any of the predefined layouts, here we choose “Colorful”.
The previous example should be modified as follows:
protected void Page_Load(object sender, EventArgs e) { COMPANYDataContext myContext = new COMPANYDataContext(); var query1 = from emp in myContext.EMPLOYEEs where ((emp.sex == 'F') || (emp.dno == 5)) select new {emp.ssn, emp.fname, emp.lname, emp.sex, emp.dno}; GridView1.DataSource = query1; GridView1.DataBind(); }
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
19
Web Demo 3. Adding a DropDown list & using its values to select data for the DataGrid.
Now we proceed to create a more complex web app. From a drop-down list the user will select a project and a
GridView will show all the employees affiliated to the selected project.
The key steps to the solution are as follows:
(a) Create a new web application call it WebLinq3. Add a “Linq to SQL class” to the application. Use the Server
Explorer to drag-drop the EMPLOYEE, WORKS_ON, and PROJECT tables to the Object-Designer Surface. Use
the Solution Explorer to show the Default.asp page. From the Toolbox drag-drop a Drop-Down list and a
GridView.
(b) Important! Change the AutoPostBack property of the drop-down list to true.
(c) the DataSource, and DataValueField attributes of the Drop-Down list will be a LINQ expression that retrieves
all the projects, and the project name (Pname) field respectively.
(d) The value DropDownList1.SelectedValue will be used in phrasing the LINQ expression producing Employees.
These changes will be detected with the .SelectedIndexChanged event of the drop-down list. The result of the
second LINQ query is the DataSource that feeds the GridView.
The final solution is
using System;
using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq; namespace WebLinq3 { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // setting the drop-down list to show project names. Do it when the // page is loaded the first time (not as a result of a client's postbak) if (!Page.IsPostBack) { COMPANYDataContext myContext = new COMPANYDataContext(); var query1 = from pRec in myContext.PROJECTs select pRec; DropDownList1.DataSource = query1; DropDownList1.DataValueField = "Pname"; DropDownList1.DataBind(); } }
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
20
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e) { COMPANYDataContext myContext = new COMPANYDataContext(); // retrieving employees working in selected project var query2 = from eRec in myContext.EMPLOYEEs from wRec in myContext.WORKS_ONs from pRec in myContext.PROJECTs where ((pRec.pname == DropDownList1.SelectedValue) && (pRec.pnumber == wRec.pno) && (wRec.essn == eRec.ssn)) select new { eRec.ssn, eRec.fname, eRec.lname, eRec.dno }; GridView1.DataSource = query2; GridView1.DataBind(); } } }
NOTE.
Must change the DropDown list
property AutoPostBack to true.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
21
Web Demo4. Complex Projection of Attributes from a Table.
In this new web-app we will list all projects and the total hours & employees working on them. Aggregation will be
used to compute sum and count values. We will „nicely‟ rename and format output columns. This is called „Shaping
Results‟.
1. Create a new web app call it: WebLinq2Sql4
2. Add | New items… | Linq to Sql classes | name it COMPANY.
3. Click on Server Explorer. Drag PROJECT, WORKS_ON, and EMPLOYEE tables to the Object-Relational
Designer.
4. Place a GridView control on the Default.asp page.
5. Add the following code-behind
protected void Page_Load(object sender, EventArgs e) { COMPANYDataContext myContext = new COMPANYDataContext(); var query1 = from w in myContext.WORKS_ONs from p in myContext.PROJECTs where (w.pno == p.pnumber) group w by new { w.pno, p.pname } into g select new { ProjID = g.Key.pno, Name = g.Key.pname, TotalHours = (decimal?)g.Sum(wr => wr.hours), TotalEmps = (int?)g.Count() }; GridView1.DataSource = query1; DataBind(); }
Note:
1. The expression (decimal?) is a cast on a nullable attribute (if null then empty).
2. .Key is an attribute produced by the GroupBy clause.
3. Observe multiple groupBy fields and selected fields made of aggregations.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
22
Web Demo5. Displaying n rows per page (Using a GridView and Skip( ) and Take( ) Methods
1. Create a new web project to show all records from the WORKS_ON table in a GridView.
2. Add a Hyperlink control to the asp page.
3. Enter the following code-behind.
using System; using System.Collections; using System.Configuration; using System.Data; using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Xml.Linq; namespace WebLinq2Sql2 { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // we add to the ASP Request.Path a variable (rowNumber) and // a value (3, 6, 9...). This pair is sent from the client to the server // each time the page visits the server int rowNumber = Convert.ToInt32( Request.QueryString["rowNumber"]); String nextNumber = (rowNumber + 3).ToString(); HyperLink1.NavigateUrl = Request.Path + "?rowNumber=" + nextNumber; MyPopulateGrid(rowNumber); } private void MyPopulateGrid(int rowNumber) { COMPANYDataContext myContext = new COMPANYDataContext(); // retrieve assignments var query1 = from w in myContext.WORKS_ONs orderby w.essn, w.pno select w; // notice the use of the Skip and Take methods GridView1.DataSource = query1.Skip(rowNumber).Take(3); DataBind(); } } }
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
23
Web Demo 6. DeclarativeASP.NET Databinding Using a LinqDataSource
This app will display/update table data without coding. Setup is made via declarative statements affecting the GUI,
DataSource, and ASP elements. Data will be displayed in a GridView from where the user will be able to insert, delete,
and modify any Employee row.
1. Create a new ASP app.
2. Create a new Entity-Model: Add | New item…| Linq to Sql classes. Call new model: CompanyEntityModel.
3. Use Server Explorer. Drag-drop EMPLOYEE and DEPARTMENT tables.
4. Save all. Right-Click on the project‟s name to Build all the elements !!!
5. Place a GridView on the Default.asp (design view) page. Click on the top-right arrow-menu button to bring
the „GridView Tasks‟ dialog box.
6. Click the dropdown control Choose DataSource and select <New data source…>. Name it:
DepEmpLinqDataSource.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
24
7. CRITICAL. A sequence of Dialog boxes will appear. On the first one select the context object:
WebLinq2Sql6A.CompanyEntityModelDataModel. Click Next.
8. The next screen allows you to choose the table to feed the GridView. Select Employee. Check all the fields
(*). Click on the OrderBy button to sort by ssn. Click on Advanced button to add delete, insert, and update
capabilities. Click OK | Finish
9. Press the Finish button.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
25
10. Improve the aesthetics of the GridView. On the Default.asp page click on the arrow-menu (top-right). Click
on Auto Format, select one option (we picked up: “Autum”).
11. Other „beautification‟ feature is to apply a format to a field. Click on Edit Columns… choose salary, on the
property panel look for Data | DataFormatString. Enter the currency format {0:c}. Remove unwanted fields
from the Selected fields: box (we removed minitial, address, bdate).
12. Tap on the GridView Tasks menu button. Select the entries Enable Sorting, Enable Editing, and Enable
Deleting. Test the application and see progress so far.
13. Instead of displaying department number (dno) we want to show department name. Click on the arrow-
menu. Select Edit columns… On the Selected fields box tap the entry dno. Now click on the link Convert this
field into a TemplateField. The property panel for dno will appear. Change the property Appearance |
HeaderText to DeptName. Change the entries Behavior | SortExpression to Department.dname. Click OK.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
26
14. Now we must change the Default.asp source script. Locate the “DeptName” entry. Replace its
<ItemTemplate> clause with the following:
<ItemTemplate>
<asp:Label ID="Label1" runat="server"
Text='<%# Eval("Department.dname") %>'></asp:Label>
</ItemTemplate>
15. The form so far looks as follows:
16. Finally we want to change the DeptName TextBox for a DropDown control, such that when we hit Edit a list
of department name choices is seen. To that effect apply the following two steps. (a) First, use Toolbox|Data
to add a new LinqDataSource, associate it with the Department table; name it DepartmentLinqDataSource. (b)
Locate in the Source page of Default.asp the entry DeptName. Replace it with the following code:
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
27
<EditItemTemplate>
<asp:DropDownList ID="DropDownList1"
DataSourceID="DepartmentLinqDataSource"
DataValueField="dnumber"
DataTextField="dname"
SelectedValue='<%#Bind("dno") %>'
runat="server"
/>
</EditItemTemplate>
17. We are ready. Run the application, work on the first row, click on the Edit link. You should see the following
image. Click on the dropdown box. Choose a new department name. Click on Update | Cancel.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
28
Web Demo7. Filtering a LinqDataSource
In this demo we will extend the previous application (Web Demo6). A dropdown box will provide a list of department
names. The user will click on one name and only employees affiliated to that department will be displayed.
1. Modify the GUI by adding a dropdown list as illustrated below (this control is called DropDownList2).
2. Click on the arrow-menu (top-right corner) of DropDownList2 to show the Tasks dialog box.
3. Click on the Configure Data Source…link.
4. The configuration wizard‟s box “Choose a Data Source” appears. (a) On the entry labeled: Select a DataSource
enter DepartmentLinqDataSource, (b) on the box Select a data field to display in the DropDownList select:
dname, (c) on the last box Select a data field value for the DropDownList select dnumber. Click OK.
5. Check the box Enable AutoPostBack of the DropDownList Tasks panel.
6. Now we must modify the GridView‟s datasource in such a way that the value in the DropDownList control determines
what employees should be displayed (dno restricted).
7. Click on the arrow-menu (top-right corner) of the DepEmpLinqDataSource control. On the LinqDataSource window
select Configure DataSource. The configuration wizard appears; click NEXT, to go to the second panel Configure Data
Selection.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
29
8. Click on the Where button. You will indicate on the next form that the Employee.dno value (displayed on the GridView
and supplied by the current data source) should be determined by the value selected in the control called
DropDownList2. See form below. Click Add button. Click Finish, respond No to next question.
9. We are ready to go. Test the application, you should see screens such as:
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
30
Part 3. Advanced Features
Custom Validation - Enforcing Business Rules.
In this demo we will implement custom validation rules on top of the Entity-Model. When the graphical model .dbml
file is created two text files are visible on the IDE: a .dbmal.layout (xml) holding a layout specification of the model and
a .designer.cs file containing the C# definition of the entities.
The second file (designer.cs) includes a number of partial classes supporting detailed operations on the entities. Here
we could write our own version of what to do in the case of validation OnValidate(…), OnChanged, OnLoaded,
onCreated, etc.
Entity-Model Diagram A fragment of the Company.designer.cs file
#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnfnameChanging(string value);
partial void OnfnameChanged();
partial void OnminitChanging(System.Nullable<char> value);
partial void OnminitChanged();
partial void OnlnameChanging(string value);
partial void OnlnameChanged();
partial void OnssnChanging(string value);
partial void OnssnChanged();
...
partial void OnaddressChanging(string value);
partial void OnaddressChanged();
partial void OnsalaryChanging(System.Nullable<double> value);
partial void OnsalaryChanged();
In this example we want to enforce the following rule:
An employee’s salary should not be higher than her direct supervisor’s salary.
Steps:
1. Create a new console application (ConLinq2Sql7). Add a “LINQ to SQL class”, call it CompanyEmp. Set the
Entity-Model diagram, holding the Employee table.
2. Use the Server Explorer to isolate a pair of employees that we will use to test the app. In our case observe
employees Smith and Wong. Their salaries are 30K and 40K respectively. Notice that Mr. Wong supervises
Mr. Smith.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
31
3. Enter the following code in the Program.cs file
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConLinq2Sql7 { class Program { static void Main(string[ ] args) { // wanting to enforce rule: // employees can not make more than their supervisors CompanyEmpDataContext myContext = new CompanyEmpDataContext(); // obtain Mr. Smith's record he make $30K // (we know his supervisor Mr. Wong makes $40K.) var emp = (from e in myContext.EMPLOYEEs where e.lname.Equals("Smith") select e).Single(); try { // try this first to test the OnValidate method added to the // extension of the EMPLOYEE class. emp.salary = 50000; //should trow exception // try this later to test rule attached to OnbdateChange method // emp.bdate = DateTime.Now.AddDays(1); myContext.SubmitChanges(); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.WriteLine("Done..."); Console.ReadLine(); } } }
4. Execute the program. Use Server Explorer to inspect the EMPLOYEE table.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
32
Observe that Smith‟s salary was changed to 50K (this is higher than Wong‟s – which is in violation to the
company‟s rules- but at this point nothing prevents it from happening!).
5. Reset Smith‟s salary to 30K (use Server Explorer > Show Table Data > retype Salary 30000).
6. Now, to enforce the business rule indicated earlier we could either write a database trigger or locally put
code in the Entity-Model to perform custom validation. We will explore the second option.
7. Enhance the EMPLOYEE definition (originally placed in CompanyEmp.designer.dbml which is part of
CompanyEmp.dbml) by adding a new (partial) class to the project. The new fragment will include the
validation logic (in the onValidate hook). Enter the following code
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConLinq2Sql7 { // This fragment extends the EMPLOYEE class definition (originally placed // in CompanyEmp.designer.dbml which is part of CompanyEmp.dbml). // Here we add a validation method to attend the "update" operation public partial class EMPLOYEE { partial void OnValidate(System.Data.Linq.ChangeAction action) { CompanyEmpDataContext myContext = new CompanyEmpDataContext(); // you should add logic for the: insert & delete cases // here we just take care of update operations if (action == System.Data.Linq.ChangeAction.Update) { //find the supervisor's record var super = (from s in myContext.EMPLOYEEs where s.ssn == this.superssn select s).Single(); //is this employee making more than her supervisor? if (this.salary > super.salary) { String errorMsg = String.Format( "VIOLATION: {0} has salary > than supervisor {1}", this.lname, super.lname); throw new Exception(errorMsg); } } }//OnValidate // Observation: // changes to specific fields can be tested using the corresponding // OnFIELDChanged() method. For example OnbdateChange follows partial void OnbdateChanged() { if (this.bdate > DateTime.Now) { throw new Exception("Invalid Birthdate"); } }//OnbdateChanged }//EMPLOYEE }
8. Execute the program (make sure Smith‟s salary initially is $30K). You should see the output:
VIOLATION: Smith has salary > than supervisor Wong
Done...
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
33
9. Modify Program.cs. Replace the line emp.salary = 50000; with emp.salary = 39000; this should not raise the
salary custom rule violation. Uncomment the birthdate assignment.
emp.salary = 39000; //should NOT trow exception
emp.bdate = DateTime.Now.AddDays(1); //to test birthdate change
10. The change in birth date raises an exception in the onbdateCanged( ) method.
Invalid Birthdate Done...
11. Neither field salary nor bdate is changed.
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
34
Demo 2. Using Stored Procedures for Create, Retrieve, Update, Delete (CRUD) operations
The goal of this demo is to show how to replace the default LINQ CRUD operations for user-defined methods.
1. Create a new project: ConLinq2SqlPart4.
2. Use Server Explorer to enter three MS-SQL-SERVER stored procedures to insert, delete, and update
WORKS_ON records. The server-side T-SQL procedures are given below
myInsertWorksOn myDeleteWorksOn myUpdateWorksOn CREATE PROCEDURE dbo.myInsertWorksOn ( @essn char(9), @pno int, @hours decimal(5,1) ) /* inserting a new assignment into WORKS_ON table */ AS insert into WORKS_ON (essn, pno, hours) values ( @essn, @pno, @hours ) RETURN
CREATE PROCEDURE dbo.myDeleteWorksOn ( @essn char(9), @pno int ) /* deleting an assignment from the WORKS_ON table */ AS delete WORKS_ON where (essn = @essn) and (pno = @pno) RETURN
CREATE PROCEDURE dbo.myUpdateWorksOn ( @essn char(9), @pno int, @hours decimal(5,1) ) /* updating an assignment in the WORKS_ON table */ AS update WORKS_ON set hours = @hours where (essn = @essn) and (pno = @pno) RETURN
3. Create the Assignments Entity-Model by dropping the WORKS_ON table to the OR-designer. Drag to the
OR-Designer Methods Panel each of the server-side stored procedures
4. Click on the WORKS_ON entity and bring the property page. Look for the methods to insert, delete,
update. Individually change their default value “Use runtime” to each of the methods created above. For
instance when changing the delete method a Configure Behavior windows appears. Select the option
Customize, use the dropdown list to choose the stored procedure myDeleteWorksOn. Click on Apply.
Repeat the process for the insert and update operations. This will update the entity‟s property page (see
image below)
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
35
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
36
5. Ready to test. Enter the following code under Program.cs.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Linq2SqlPart4 { class Program { static void Main(string[] args) { // tesing the custom-made UPDATE procedure using (ASSIGNMETSDataContext myContext = new ASSIGNMETSDataContext()) { var task = (from w in myContext.WORKS_ONs where (w.essn == "123456789") && (w.pno == 1) select w).Single(); Console.WriteLine("BEFORE hours {0}", task.hours); task.hours = 60; myContext.SubmitChanges(); } using (ASSIGNMETSDataContext myContext = new ASSIGNMETSDataContext()) { var task = (from w in myContext.WORKS_ONs where (w.essn == "123456789") && (w.pno == 1) select w).Single(); Console.WriteLine("AFTER hours {0}", task.hours); } // testing custom-made INSERT method using (ASSIGNMETSDataContext myContext = new ASSIGNMETSDataContext()) { try { // place 123456789 on project 3 for 1.8 hours WORKS_ON wRec = new WORKS_ON { essn = "123456789", pno = 3, hours = (decimal)1.8 }; myContext.WORKS_ONs.InsertOnSubmit(wRec); myContext.SubmitChanges(); Console.WriteLine("AFTER Inserting"); } catch (Exception ex) { Console.WriteLine("Problems inserting\n" + ex.Message); } }
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
37
// testing custom-made DELETE method using (ASSIGNMETSDataContext myContext = new ASSIGNMETSDataContext()) { try { // remove 123456789 from project 3 var wRec = myContext.WORKS_ONs.Single( w => (w.essn == "123456789") && (w.pno == 3)); myContext.WORKS_ONs.DeleteOnSubmit(wRec); myContext.SubmitChanges(); Console.WriteLine("AFTER deleting"); } catch (Exception ex) { Console.WriteLine("Problems deleting\n" + ex.Message); } } Console.WriteLine("\nDone..."); Console.ReadLine(); } } }
6. Use Server Explorer to inspect results in the database
7. Done!
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
38
Appendix A. MS-SQL SERVER - Company Database
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
39
Appendix B. A Fragment of the DataContext definition for the WORKS_ON Entity.
[Table(Name="dbo.WORKS_ON")] public partial class WORKS_ON : INotifyPropertyChanging, INotifyPropertyChanged { private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty); private string _essn; private int _pno; private System.Nullable<decimal> _hours; private EntityRef<EMPLOYEE> _EMPLOYEE; private EntityRef<PROJECT> _PROJECT; #region Extensibility Method Definitions partial void OnLoaded(); partial void OnValidate(System.Data.Linq.ChangeAction action); partial void OnCreated(); partial void OnessnChanging(string value); partial void OnessnChanged(); partial void OnpnoChanging(int value); partial void OnpnoChanged(); partial void OnhoursChanging(System.Nullable<decimal> value); partial void OnhoursChanged(); #endregion public WORKS_ON() { this._EMPLOYEE = default(EntityRef<EMPLOYEE>); this._PROJECT = default(EntityRef<PROJECT>); OnCreated(); } [Column(Storage="_essn", DbType="Char(9) NOT NULL", CanBeNull=false, IsPrimaryKey=true)] public string essn { get { return this._essn; } set { if ((this._essn != value)) { if (this._EMPLOYEE.HasLoadedOrAssignedValue) { throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException(); } this.OnessnChanging(value); this.SendPropertyChanging(); this._essn = value; this.SendPropertyChanged("essn"); this.OnessnChanged(); } } } [Column(Storage="_pno", DbType="Int NOT NULL", IsPrimaryKey=true)] public int pno { get { return this._pno; } set { if ((this._pno != value)) { if (this._PROJECT.HasLoadedOrAssignedValue) {
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
40
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException(); } this.OnpnoChanging(value); this.SendPropertyChanging(); this._pno = value; this.SendPropertyChanged("pno"); this.OnpnoChanged(); } } } [Column(Storage="_hours", DbType="Decimal(5,1)")] public System.Nullable<decimal> hours { get { return this._hours; } set { if ((this._hours != value)) { this.OnhoursChanging(value); this.SendPropertyChanging(); this._hours = value; this.SendPropertyChanged("hours"); this.OnhoursChanged(); } } } [Association(Name="EMPLOYEE_WORKS_ON", Storage="_EMPLOYEE", ThisKey="essn", IsForeignKey=true)] public EMPLOYEE EMPLOYEE { get { return this._EMPLOYEE.Entity; } set { EMPLOYEE previousValue = this._EMPLOYEE.Entity; if (((previousValue != value) || (this._EMPLOYEE.HasLoadedOrAssignedValue == false))) { this.SendPropertyChanging(); if ((previousValue != null)) { this._EMPLOYEE.Entity = null; previousValue.WORKS_ONs.Remove(this); } this._EMPLOYEE.Entity = value; if ((value != null)) { value.WORKS_ONs.Add(this); this._essn = value.ssn; } else { this._essn = default(string); } this.SendPropertyChanged("EMPLOYEE"); } } } [Association(Name="PROJECT_WORKS_ON", Storage="_PROJECT", ThisKey="pno", IsForeignKey=true)] public PROJECT PROJECT { get { return this._PROJECT.Entity; } set { PROJECT previousValue = this._PROJECT.Entity; if (((previousValue != value) || (this._PROJECT.HasLoadedOrAssignedValue == false))) {
Cleveland State University CIS611 – LINQ to SQL Lecture Notes – Prof. V. Matos
41
this.SendPropertyChanging(); if ((previousValue != null)) { this._PROJECT.Entity = null; previousValue.WORKS_ONs.Remove(this); } this._PROJECT.Entity = value; if ((value != null)) { value.WORKS_ONs.Add(this); this._pno = value.pnumber; } else { this._pno = default(int); } this.SendPropertyChanged("PROJECT"); } } } public event PropertyChangingEventHandler PropertyChanging; public event PropertyChangedEventHandler PropertyChanged; protected virtual void SendPropertyChanging() { if ((this.PropertyChanging != null)) { this.PropertyChanging(this, emptyChangingEventArgs); } } protected virtual void SendPropertyChanged(String propertyName) { if ((this.PropertyChanged != null)) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }