Archive

Posts Tagged ‘POCO’

IronRuby with LINQ To SQL (kinda)

October 16th, 2008 Simon Segal No comments

I have been playing around with IronRuby a bit just of late and I was curious to see whether it was at a stage where it was working with LINQ To SQL. It was clear from the outset that using LINQ To SQL in the most common way (with full UI experience) in VS.Net was simply a silly thing to expect and it became immediately apparent that any benefit of IEnumerable<T> or IQueryable<T> was out the window also. Given that that any attempt to access IEnumerable<T> from any instance of Table<T> was for now a journey into folly, I tried briefly to circumvent that approach and see if I could POCO my way to an outcome with Mapping files. Listing 1.0 shows this unfortunately doomed venture.

Attempt No # 1.0

First the C# POCO Entity class required in the RubyCustomer library.

using System;

namespace Org.TechAvalanche.Orm.Tests.Domain
{
    public class Customer
    {
        private string _CustomerID;
        private string _CompanyName;
        private string _ContactName;
        private string _ContactTitle;
        private string _Address;
        private string _City;
        private string _Region;
        private string _PostalCode;
        private string _Country;
        private string _Phone;
        private string _Fax;

        public Customer() { }

        public string CustomerID
        {
            get {return this._CustomerID;}
            set {this._CustomerID = value;}
        }

        public string CompanyName
        {
            get {return this._CompanyName;}
            set {this._CompanyName = value;}
        }

        public string ContactName
        {
            get {return this._ContactName;}
            set {this._ContactName = value;}
        }

        public string ContactTitle
        {
            get {return this._ContactTitle;}
            set {this._ContactTitle = value;}
        }

        public string Address
        {
            get {return this._Address;}
            set {this._Address = value;}
        }

        public string City
        {
            get {return this._City;}
            set {this._City = value;}
        }

        public string Region
        {
            get {return this._Region;}
            set {this._Region = value;}
        }

        public string PostalCode
        {
            get {return this._PostalCode;}
            set {this._PostalCode = value;}
        }

        public string Country
        {
            get {return this._Country;}
            set {this._Country = value;}
        }

        public string Phone
        {
            get {return this._Phone;}
            set {this._Phone = value;}
        }

        public string Fax
        {
            get {return this._Fax;}
            set {this._Fax = value;}
        }
    }
}

coupled with the following LINQ To SQL Mapping file

<?xml version=”1.0″ encoding=”utf-8″?>
<Database Name=”Northwind”
    xmlns=”http://schemas.microsoft.com/linqtosql/mapping/2007″>
  <Table Name=”Customers” Member=”Customer”>
    <Type Name=”Org.TechAvalanche.Orm.Tests.Domain.Customer”>
      <Column Name=”CustomerID” Member=”CustomerID” DbType=”NChar(5) NOT NULL”
          IsPrimaryKey=”true” CanBeNull=”false” UpdateCheck=”Never” />
      <Column Name=”CompanyName” Member=”CompanyName”
          DbType=”NVarChar(40) NOT NULL” CanBeNull=”false” UpdateCheck=”Never” />
      <Column Name=”ContactName” Member=”ContactName”
          DbType=”NVarChar(30)” CanBeNull=”true” UpdateCheck=”Never” />
      <Column Name=”ContactTitle” Member=”ContactTitle”
          DbType=”NVarChar(30)” CanBeNull=”true” UpdateCheck=”Never” />
      <Column Name=”Address” Member=”Address”
          DbType=”NVarChar(60)” CanBeNull=”true” UpdateCheck=”Never” />
      <Column Name=”City” Member=”City”
          DbType=”NVarChar(15)” CanBeNull=”true” UpdateCheck=”Never” />
      <Column Name=”Region” Member=”Region”
          DbType=”NVarChar(15)” CanBeNull=”true” UpdateCheck=”Never” />
      <Column Name=”PostalCode” Member=”PostalCode”
          DbType=”NVarChar(10)” CanBeNull=”true” UpdateCheck=”Never” />
      <Column Name=”Country” Member=”Country”
          DbType=”NVarChar(15)” CanBeNull=”true” UpdateCheck=”Never” />
      <Column Name=”Phone” Member=”Phone”
          DbType=”NVarChar(24)” CanBeNull=”true” UpdateCheck=”Never” />
      <Column Name=”Fax” Member=”Fax”
          DbType=”NVarChar(24)” CanBeNull=”true” UpdateCheck=”Never” />
    </Type>
  </Table>
</Database>

and now our IronRuby attempt to talk DIRECTLY LINQ To SQL.

require ‘mscorlib’
require ‘System.Core, Version=3.5.0.0,
  Culture=neutral, PublicKeyToken=b77a5c561934e089′
require ‘System.Windows.Forms, Version=2.0.0.0,
  Culture=neutral, PublicKeyToken=b77a5c561934e089′
require ‘System.Data.Linq, Version=3.5.0.0,
  Culture=neutral, PublicKeyToken=b77a5c561934e089′
require ‘RubyCustomer, Version=0.0.0.0,
  Culture=neutral, PublicKeyToken=c1aba19d9dedea39′

begin

  #get the mapping into a stream
  stream = System::IO::StreamReader.new(“C:\\Users\\simon.segal” +
    “\\Desktop\\IronRuby\\ironruby\\bin\\DomainMapping.xml”)
  mapping_file =
    System::Data::Linq::Mapping::XmlMappingSource.
      FromStream(stream.BaseStream)
  #new up a DataContext with our mappings
  db = System::Data::Linq::DataContext.new(“server=localhost;initial “+
    “catalog=Northwind;user id=sa;password=supremo”, mapping_file)
  #new up a POCO customer from my C# library
  cust = Org::TechAvalanche::Orm::Tests::Domain::Customer.new
  #not sure if typeof works in IronRuby
  cust_type = cust.GetType()
  #load the table of customers from northwind
  custs = db.GetTable(cust_type)
  #tried to get enumerator but .Current gets a method error 
  #and so ends our journey
  list = custs.GetEnumerator()
  puts “The call to GetEnumerator returned a “ +
    list.GetType.ToString()
  currentCust = list.Current
  rescue Exception => ex
    puts “someone is missing a method!” + ex.to_s
    #just to prove that it’s a Table<Customer>
    puts “Just to show that we did get as far as “ +
           “getting the Table<Customer> : “ +
           custs.ToString()

end

Which yields the following output from standard IO (click to see full view):

ironLinqToSqlFailedOutput

So as you can see here, we get close but no cigar. Ready to give up - damn, not yet! Lets move all the LINQ To SQL code into our custom library and call it from IronRuby - that should work? We already had a POCO in our library referenced above (see the require ‘RubyCustomer’). So here we go.

Attempt No # 2.0

As a jumping off point for attempt # 2, I thought it might be time to check out Ruby In Steel, a third party integrated UI platform for VS.NET and IronRuby. I wanted to be able to keep my C# library with the LINQ To SQL code and the IronRuby consumer code in a single solution if possible and I was looking for a bit of holiday from writing Ruby Code in SciTE and NetBeans, therefore looking at Ruby In Steel had been on the agenda for a while so I figured now was as good a time as any. This gave me a more instantly gratifying experience in developing a Windows Form to present my LINQ To SQL data as well:

Ruby In Steel

As you can see I have my C# project and my IronRuby project coupled together in a single VS.Net solution and whilst Ruby in Steel is not quite as stable or fully featured as you would expect it to be when IronRuby makes it’s official debut, it certainly helped speed things up quite a bit.

The LINQ To SQL code consisted of a run of the mill repository, POCO and mapping file (the entity itself remained as is - see above Customer Class).

using System.Data.Linq;
using System.Linq;

namespace CsharpLinqToSqlForIronRuby
{
    public class CustomerRepository
    {
        private readonly DataContext _context = null;

        public CustomerRepository(DataContext ctx)
        {
            _context = ctx;
        }

        public Customer[] All()
        {
            Customer[] retCustomers = null;
            using (_context)
            {
                retCustomers = _context.GetTable<Customer>().ToArray();
            }
            return retCustomers;
        }

        public Customer FindById(string custId)
        {
            Customer retCustomer = null;

            using (_context)
            {
                retCustomer = _context.GetTable<Customer>().
                    Where(c => c.CustomerID == custId).Single();
            }
            return retCustomer;
        }
    }
}

On the IronRuby side of things, we dialled up a class to handle calling our C# Repository library:

require ‘mscorlib’
require ‘System.Core, Version=3.5.0.0, ‘ +
    ‘Culture=neutral, PublicKeyToken=b77a5c561934e089′
require ‘System.Windows.Forms, Version=2.0.0.0, ‘ +
    ‘Culture=neutral, PublicKeyToken=b77a5c561934e089′
require ‘System.Data.Linq, Version=3.5.0.0, ‘ +
    ‘Culture=neutral, PublicKeyToken=b77a5c561934e089′
require ‘D:/simon.segal/LocalWorking/IronRubyProofs/’ +
    ‘IronRubyWithLinqToSqlForms/IronRubyWithLinqToSqlForms/’ +
    ‘CsharpLinqToSqlForIronRuby.dll’

class CustomerController

    def GetAllCustomers

        #get a stream of the mapping file
        stream = System::IO::StreamReader.new(“D:\\simon.segal\\” +
            “LocalWorking\\IronRubyProofs\\” +
            “IronRubyWithLinqToSqlForms\\” +
            “IronRubyWithLinqToSqlForms\\DomainMapping.xml“)
        #set the mapping source
        mapping_file = System::Data::Linq::Mapping::XmlMappingSource.
            FromStream(stream.BaseStream)
        #new up a DataContext with our mapping
        db = System::Data::Linq::DataContext.new(”server=localhost;initial ” +
            “catalog=Northwind;user id=sa;password=supremo”, mapping_file)
        #new up our repository
        cust_repos = CsharpLinqToSqlForIronRuby::CustomerRepository.new(db)
        #get all the customers
        custs = cust_repos.All()
        #print customers to the console window (in debug)
        custs.each {|cust| puts cust.CompanyName}

        return custs

    end

end

And what we needed now was an IronRuby WinForm to do the presentation work and data binding for us:

ironrubyForm

with the following code behind:
require ‘Form1.designer.rb’
require ‘CustomerController.rb’

class Form1

    #form load
    def Form1_Load(sender, e)
    end

    #button click event handler
    def button1_Click(sender, e)

        controller = CustomerController.new
        self.comboBox1.DisplayMember = “CustomerID”
        self.comboBox1.ValueMember = “CustomerID”
        custs = controller.GetAllCustomers.each {|customer|
            self.comboBox1.Items.Add(customer)}
        self.lstCustomer.DataSource = custs
        self.lstCustomer.DisplayMember = “CustomerID”
        self.lstCustomer.ValueMember = “ContactName”

    end

    #combo box selected index changed handler
    def comboBox1_SelectedIndexChanged(sender, e)
        cust = self.comboBox1.SelectedItem
        self.textBox1.Text = cust.CompanyName
        self.textBox2.Text = cust.ContactName
    end

    #list customer selected index changed handler
    def lstCustomer_SelectedIndexChanged(sender, e)
        cust = self.lstCustomer.SelectedItem
        self.textBox1.Text = cust.CompanyName
        self.textBox2.Text = cust.ContactName
    end

end
So unfortunately right at this moment there is no IronRuby to be had with LINQ To SQL unless it’s via one of the other .NET languages that support it. I would have been more than happy if I had been able to leverage my own custom Repository Framework to do the data access but that isn’t possible with the current limitations either.

Share/Save/Bookmark

Categories: IronRuby, Languages, ORM, POCO Tags: , , ,

LINQ To SQL - Going POCO and more……! Part 2.

September 27th, 2008 Simon Segal No comments

In part 1 of this post I made the code available for a framework that I named Golden Gate, which provided the pieces to use a POCO approach in using the LINQ To SQL OR/M along with Specifications (for dynamic querying and equality testing) and Fetching Strategies (providing flexible load options to suit the eager or lazy load requirements). What was missing in the code download was an example that utilised the DataContext classes ability to provide lazy loading or deferred loading as it terms it. The code below demonstrates how to create your POCO entities to allow for this.

//For the One to Many side of a relationship

private EntitySet<RingtailPage> _pages;

public IList<RingtailPage> Pages
{
    get { return _pages; }
    set { _pages.Assign(value); }
}

//For the Many to One side of a relationship

private EntityRef<RingtailDocument> _document;

public RingtailDocument Document
{
    get { return _document.Entity; }
    set { _document.Entity = value; }
}

Whilst the use of the EntitySet<T> and EntityRef<T> dont exactly fit the purist POCO approach, it’s a small price to pay when you consider that access to them are wrapped by an accessor mutator that exposes the Entity in question or an IList<T> where T is the entity in question. Matthew Charles explains why EntitySet<T> and EntityRef<T> are required for LINQ To SQL lazy loading.

Share/Save/Bookmark

Categories: LINQ To SQL, ORM, POCO Tags: , ,

POCO improves inheritance for LINQ to SQL

August 26th, 2008 Simon Segal 1 comment

Have you ever needed to use inheritance with LINQ To SQL? The designer experience isn’t exactly what your OO professor probably had in mind when it comes to modelling inheritance and to honest the whole process feels wrong. Let me explain.

I don’t like the whole ‘partial’ class approach to inheritance to begin with. I want the freedom to inherit without the need for using Partial classes and better still I don’t want my business objects to be tied to the LINQ To SQL platform. If we consider the following scenario where my LINQ To SQL designer experience looks like this:

Figure 1.0

L2SDesigner

What figure 1.0 demonstrates is the we have an employee entity, from which three other entities all inherit. SalaryEmployee, CommissionEmployee and ContractEmployee all have employee as their base class. In the case of SalaryEmployee, it distinctly encapsulates a property for the middle initial via the property ‘minit’. Sure the designer looks great but from the perspective of the code, the designer has generated things don’t look so nice to me, as demonstrated in the following code listing.

Code Listing 1.0

[Table(Name="dbo.employee")]
[InheritanceMapping(Code="1", Type=typeof(SalaryEmployee)]
[InheritanceMapping(Code="2", Type=typeof(CommissionEmployee))]
[InheritanceMapping(Code="3", Type=typeof(ContractEmployee))]
public partial class employee : INotifyPropertyChanging,
                                INotifyPropertyChanged
{
    //…….
}

public partial class SalaryEmployee : employee
{
    private System.Nullable<char> _minit;

    #region Extensibility Method Definitions

    partial void OnLoaded();
    partial void OnValidate(System.Data.Linq.ChangeAction action);
    partial void OnCreated();
    partial void OnminitChanging(System.Nullable<char> value);
    partial void OnminitChanged();

    #endregion

    public SalaryEmployee()
    {
        OnCreated();
    }

    [Column(Storage="_minit", DbType="Char(1)")]
    public System.Nullable<char> minit
    {
        get
        {
            return this._minit;
        }
        set
        {
            if ((this._minit != value))
            {
                this.OnminitChanging(value);
                this.SendPropertyChanging();
                this._minit = value;
                this.SendPropertyChanged("minit");
                this.OnminitChanged();
            }
        }
    }
}

public partial class CommissionEmployee: employee
{
    //…….
}

public partial class ContractEmployee: employee
{
    //…….
}

As demonstrated above, the designer generates code that comes decorated with quite a bit of LINQ To SQL infrastructure attributes or noise as I like to call it, along with some partial methods that developers can implement in their own partial classes designed to extend those generated by the designer. I have included the entire code generated for the SalaryEmployee entity for the sake of demonstration and to show that further LINQ To SQL infrastructure code has been used to decorate the entity. Now it’s very important to point out here, that this code should NOT be touched in the IDE and it’s linked inextricably to the designer, so if I just start to add polymorphic behaviours or new members to the entities via the generated code, then the moment I refresh or add anything via the designer, it will blow away any changes made by directly to the code. Microsoft have designed the entity classes in a such a way that by using partial classes we can implement a subsequent file to that provided by the code generator and implement all of our new behaviours and members using our own hand rolled partial class. I could do something like this:

Code Listing 2.0

public interface IEmployee
{
    bool QualifiesAsPrefered();
}

public partial class employee : IEmployee
{
    #region IEmployee Members

    public virtual bool QualifiesAsPrefered()
    {
        return false;
    }

    #endregion
}

I could create this code above in a new code file and thus extend my designer generated entities by using an interface to describe any polymorphic intent against my partial class. This does begin to become unwieldy in my opinion and I consider the use of the partial class a bit of code smell to be blunt and its really only required because of the need to maintain synchronicity between the code generated entity and the designer.

Now this is not to say that developers should not use the designer and take the approach just demonstrated, in fact I should point out that given the right circumstances, such an approach is completely justified. Lets say you work for a small or medium sized project driven business and there is a need to write a data driven application that contains less than significant business logic, has low support requirements, a small user audience and is required in the shortest amount of time possible. Sounds familiar right! Well those are typical times when you might want to do just that. However, what about if I have a team of 6 - 8 developers on a project spanning eight months and the application is driven heavily by data? This is the kind of circumstance that this approach just wont feel comfortable and will cause you headaches because it’s not particularly reusable or portable across a large team, it also leads to business entities getting tied to a Data Access strategy and will have the developer team dragging around all that infrastructure code with them. What is this going to mean for sharing entities within services or what are my serialisation concerns and should I consider breaking my partial classes into separate projects and…….you can see where this is leading, more confusion and pain for all. So what’s the solution?

By using a POCO (Plain Old CLR Object) approach (see DDD) along with LINQ To SQL mapping, we can choose to use our tried and trusted OO skills to describe our classes (entities), cutting down on the number of artifacts it takes to achieve that and in so doing simplify our lives by making the code less ambiguous. So how different would that be? Lets start with the mapping file used to map entities to tables in the database.

Code Listing 3.0


<Database Name="Northwind"
    xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
  <Table Name="Customers" Member="Customer">
    <Type Name="Org.TechAvalanche.Orm.Tests.Domain.Customer">
      <Column Name="CustomerID" Member="CustomerID"
        DbType="NChar(5)
            NOT NULL" IsPrimaryKey="true"
        CanBeNull="false" UpdateCheck="Never" />
      <Column Name="CompanyName" Member="CompanyName"
        DbType="NVarChar(40)
            NOT NULL"
        CanBeNull="false" UpdateCheck="Never" />
      <Column Name="ContactName" Member="ContactName"
        DbType="NVarChar(30)"
        CanBeNull="true" UpdateCheck="Never" />
      <Column Name="ContactTitle" Member="ContactTitle"
        DbType="NVarChar(30)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="Address" Member="Address"
        DbType="NVarChar(60)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="City" Member="City"
        DbType="NVarChar(15)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="Region" Member="Region"
        DbType="NVarChar(15)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="PostalCode" Member="PostalCode"
        DbType="NVarChar(10)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="Country" Member="Country"
        DbType="NVarChar(15)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="Phone" Member="Phone"
        DbType="NVarChar(24)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="Fax" Member="Fax"
        DbType="NVarChar(24)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="CustType" Member="CustomerType"
        DbType="Int" CanBeNull="false" IsDiscriminator="true"
        UpdateCheck="Never" />
      <Association Name="Customer_Order"
        Member="Orders" OtherKey="CustomerID" />

      <!– These Two Elements used for inheritance –>
      <Type
        Name="Org.TechAvalanche.Orm.Tests.Domain.InternalCustomer"
        InheritanceCode="1" IsInheritanceDefault="true"/>
      <Type
        Name="Org.TechAvalanche.Orm.Tests.Domain.ExternalCustomer"
        InheritanceCode="2" IsInheritanceDefault="false"/>
    </Type>
  </Table>
  <Table Name="Orders" Member="Order">
    <Type Name="Org.TechAvalanche.Orm.Tests.Domain.Order">
      <Column Name="OrderID" Member="OrderID"
        DbType="Int NOT NULL
            IDENTITY" IsPrimaryKey="true"
        IsDbGenerated="true" CanBeNull="false"
        UpdateCheck="Never" />
      <Column Name="CustomerID" Member="CustomerID"
        DbType="NChar(5)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="EmployeeID" Member="EmployeeID"
        DbType="Int" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="OrderDate" Member="OrderDate"
        DbType="DateTime" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="RequiredDate" Member="RequiredDate"
        DbType="DateTime" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="ShippedDate" Member="ShippedDate"
        DbType="DateTime" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="ShipVia" Member="ShipVia"
        DbType="Int" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="Freight" Member="Freight"
        DbType="Money" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="ShipName" Member="ShipName"
        DbType="NVarChar(40)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="ShipAddress" Member="ShipAddress"
        DbType="NVarChar(60)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="ShipCity" Member="ShipCity"
        DbType="NVarChar(15)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="ShipRegion" Member="ShipRegion"
        DbType="NVarChar(15)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="ShipPostalCode" Member="ShipPostalCode"
        DbType="NVarChar(10)" CanBeNull="true"
        UpdateCheck="Never" />
      <Column Name="ShipCountry" Member="ShipCountry"
        DbType="NVarChar(15)" CanBeNull="true"
        UpdateCheck="Never" />
      <Association Name="Order_OrderLine" Member="OrderLines"
        OtherKey="OrderID" />
      <Association Name="Customer_Order" Member="Customer"
        ThisKey="CustomerID" IsForeignKey="true" />
    </Type>
  </Table>
  <Table Name="[Order Details]" Member="OrderLine">
    <Type Name="Org.TechAvalanche.Orm.Tests.Domain.OrderLine">
      <Column Name="OrderID" Member="OrderID"
        DbType="Int NOT NULL" IsPrimaryKey="true"
        CanBeNull="false" UpdateCheck="Never" />
      <Column Name="ProductID" Member="ProductID"
        DbType="Int NOT NULL" IsPrimaryKey="true"
        CanBeNull="false" UpdateCheck="Never" />
      <Column Name="UnitPrice" Member="UnitPrice"
        DbType="Money NOT
            NULL" CanBeNull="false"
        UpdateCheck="Never" />
      <Column Name="Quantity" Member="Quantity"
        DbType="SmallInt
            NOT NULL" CanBeNull="false"
        UpdateCheck="Never" />
      <Column Name="Discount" Member="Discount"
        DbType="Real NOT
            NULL" CanBeNull="false"
        UpdateCheck="Never" />
      <Association Name="Order_OrderLine" Member="Order"
        ThisKey="OrderID" IsForeignKey="true" />
    </Type>
  </Table>
</Database>

The two entries above that sit directly beneath the comment highlighted in large font demonstrate how to set up mapping XML for regular DotNet CLR objects that are NOT generated by the designer and account for inheritance. The entity code to go with this mapping follows below.

Code Listing 4.0

interface ICustomerData
{
    string Address { get; set; }
    string City { get; set; }
    string CompanyName { get; set; }
    string ContactName { get; set; }
    string ContactTitle { get; set; }
    string Country { get; set; }
    string CustomerID { get; set; }
    string Fax { get; set; }
    IList<Order> Orders { get; set; }
    string Phone { get; set; }
    string PostalCode { get; set; }
    string Region { get; set; }
    int CustomerType { get; set; }
}

public interface ICustomerLoyaltyDiscount
{
    void LoyaltyDiscount();
}

interface ICustomer : ICustomerData, ICustomerLoyaltyDiscount { }

public class Customer : ICustomer
{
    public void LoyaltyDiscount()
    {
        foreach (Order o in Orders)
        {
            o.Freight = 0M;
        }
    }
}

public class InternalCustomer : Customer
{
    //………etc
} 

public class ExternalCustomer : Customer
{
    //………etc
}

If you really must have a designer to view and work with your entities then it’s no sweat to create a class diagram in VS.Net and enjoy a similar design experience as you would with the LINQ To SQL designer however enjoy the benefits of going POCO at the same time.

Figure 2.0

classDesigPoco

The consumer code would look something like this:

public void SimpleRepositoryFetchingStrategyTest()
{
    Specification<Customer> simonsCoSpec =
        new Specification<Customer>
        (c => c.Country == "Germany");

    DataContext ctx = GetContext();

    Repository<Customer> customerRepository =
        new Repository<Customer>(ctx);

    CustomerLoyaltyDiscountFetchingStrategy strategy =
        new CustomerLoyaltyDiscountFetchingStrategy();

    var custs = customerRepository.All(simonsCoSpec, strategy);

    foreach (var cust in custs)
    {
        cust.LoyaltyDiscount();
        Console.WriteLine("The Name of the Customer " +
            "from Germany is {0}", cust.CompanyName);
        foreach (var order in cust.Orders)
        {
            Console.WriteLine("\tOrder Number {0}",
                order.OrderID);
            foreach (var orderLine in order.OrderLines)
            {
                Console.WriteLine("\t\tProduct ID {0} Amount {1}",
                        orderLine.ProductID,
                        (orderLine.Quantity * orderLine.UnitPrice));
            }
        }
    }
}
private static DataContext GetContext()
{
    //get a stream reader from the mapping file

    StreamReader sr =
        new StreamReader(@"D:\Mapping\DomainMapping.xml");

    //set the mapping source up
    XmlMappingSource mapping =
        XmlMappingSource.FromStream(sr.BaseStream);

    DataContext db = null;

    //new up a persistance repository
    db = new DataContext
        ("server=localhost;initial catalog=Northwind;" +
        "user id=sa;password=mrbig", mapping);

    return db;
}

I will shortly be posting a framework that makes working in a POCO and DDD style much simpler. The Framework includes implemented Specification Patterns, Fetching Strategies and Extensible Repositories for your LINQ To SQL pleasure.

Share/Save/Bookmark

Categories: C#, DDD, LINQ To SQL, ORM, POCO Tags: , , ,
Creative Commons Attribution-ShareAlike 2.5 Australia
Creative Commons Attribution-ShareAlike 2.5 Australia