LINQ Code

Let’s examine exactly what has taken place behind the scenes as a result of the drag-and-drop operation between Server Explorer and the O/R designer.

For one, the connection string necessary to open a connection to the selected database is automatically stored in the app.config (or web.config) file for you. This is then leveraged by LINQ to make calls into the database when needed. In addition, a new class has been defined, in this case one named PurchaseOrderHeader.


Note

The O/R designer actually “depluralizes” entity names for you automatically. Many HR databases, for instance, choose to implement an employee table and call it Employees because it stores the data records for more than one worker. In an attempt to further push through the object model to data model impedance mismatch, the O/R designer actually creates a class called Employee and not Employees; this correlates much better with the true intent of the class (which is to contain a single row/instance from the table and not the entire table).


If you look at the resulting LINQ code (in our example, by viewing the code inside the file DataClasses1.designer.cs), you will see that LINQ marks up the object model with attributes to perform the magic linking between the objects and the database. Via the Table attribute, this class has been identified as a direct map to the Purchasing.PurchaseOrderHeader table.

Each column in the PurchaseOrderHeader table has also been implemented as a property on the Employee class. This snippet shows the PurchaseOrderID property:

[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_PurchaseOrderID",
AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true,
IsDbGenerated=true)]
public int PurchaseOrderID
{
       get
       {
              return this._PurchaseOrderID;
       }
       set
       {
              if ((this._PurchaseOrderID != value))
              {
                     this.OnPurchaseOrderIDChanging(value);
                     this.SendPropertyChanging();
                     this._PurchaseOrderID = value;
                     this.SendPropertyChanged("PurchaseOrderID");
                     this.OnPurchaseOrderIDChanged();
              }
       }
}

Beyond the PurchaseOrderHeader class, there has also been code generated for the data context. Here is a snippet of the class definition created automatically for us.

[global::System.Data.Linq.Mapping.DatabaseAttribute(Name="AdventureWorks2008")]
public partial class DataClasses1DataContext : System.Data.Linq.DataContext
{
   private static System.Data.Linq.Mapping.MappingSource mappingSource
      = new AttributeMappingSource();
#region Extensibility Method Definitions
partial void OnCreated();
partial void InsertPurchaseOrderHeader(PurchaseOrderHeader instance);
partial void UpdatePurchaseOrderHeader(PurchaseOrderHeader instance);
partial void DeletePurchaseOrderHeader(PurchaseOrderHeader instance);
#endregion
       public DataClasses1DataContext() :
           base(global::AWL2S.Properties.Settings.Default.
           AdventureWorks2008ConnectionString, mappingSource)
          {
              OnCreated();
          }
       public DataClasses1DataContext(string connection) :
           base(connection, mappingSource)
          {
              OnCreated();
          }
       public DataClasses1DataContext(System.Data.IDbConnection connection) :
           base(connection, mappingSource)
          {
              OnCreated();
          }
       public DataClasses1DataContext(string connection,
            System.Data.Linq.Mapping.MappingSource mappingSource) :
            base(connection, mappingSource)
          {
              OnCreated();
          }
       public DataClasses1DataContext(System.Data.IDbConnection connection,
          System.Data.Linq.Mapping.MappingSource mappingSource) :
          base(connection, mappingSource)
          {
              OnCreated();
          }
       public System.Data.Linq.Table<PurchaseOrderHeader>
          PurchaseOrderHeaders
       {
          get
          {
             return this.GetTable<PurchaseOrderHeader>();
          }
       }
}

You can think of DataContext as the LINQ manager: it handles the connection back to the database, manages the in-memory entities, and marshals the calls necessary for data updates and any issues that might arise from concurrency and locking conflicts. In total, more than 500 lines of functioning code were emitted to make all this work. So how do you actually use a LINQ object within your application? Read on.

Working with LINQ Objects

The goal with LINQ, again, is simplicity; LINQ classes look and behave just like any other class in our object model. If we wanted to add a new employee to the system, we would create a new Employee object and set its properties like this.

Employee emp = new Employee();

emp.BirthDate = new DateTime(1965, 4, 4);
emp.Gender = 'F';
emp.LoginID = "templogin";
emp.MaritalStatus = 'M';
emp.Title = "Project Resource Manager";
...

To commit this new Employee object to the Employee table, we need to add the object to the Employees collection held by our data context and then call the SubmitChanges method. Remember that the type is simply the default name given by the O/R designer to our data context class; we can change this to anything we want.

DataClasses1DataContext db = new DataClasses1DataContext();
db.Employees.InsertOnSubmit(emp);
db.SubmitChanges();

In a similar fashion, employees can be removed from the collection (and then from the database).

db.Employees.DeleteOnSubmit(emp);
db.SubmitChanges();

We have really just scratched the surface here with regard to the intricacies and complexities of O/R application development using LINQ; but hopefully this overview of the O/R designer can be used as a starting point for your O/R explorations in Visual Studio. Let’s move on to the Entity Framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset