[html-post] Where the Demo ends...

  • Thread starter Thread starter Michael Starberg
  • Start date Start date
M

Michael Starberg

Hi folks.
First up, sorry for sporting html, but I want the code to be nice below.

I typically educate/demo LINQ/.NET 3.5 and C# 3.0 to all who wants to know. At work that is for our coders, and for our customers; well.. they are curious... and pays for it.

My style is to sit at my laptop, connected to a projector, and let the curious watch me as I talk/explain and type away. What I typically end is what you can see below.

As for the bright dudes here; - What could I do better? Or Different? OrElse?

Any comments are appreciated. Constructive critque are welcome. Heck, even flames and insults are welcome.

My question is; - How would you change the demo-code below?

Happy Coding
-.Michael Starberg

-------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace ConsoleDemo
{
#region Program
class Program
{
static void Main(string[] args)
{
(new Demo()).DoStuff();
Console.WriteLine();
Console.WriteLine("Done...");
Console.ReadLine();
}
}
#endregion

public class Demo
{
public void DoStuff()
{
var db = new NorthwndDataContext();

db.DeferredLoadingEnabled = false;
db.ObjectTrackingEnabled = false;

var lo = new DataLoadOptions();
lo.LoadWith<Customer>(c => c.Orders);
lo.LoadWith<Order>(o => o.Order_Details);
lo.LoadWith<Order_Detail>(od => od.Product);

lo.AssociateWith<Customer>(c =>
from o in c.Orders
where o.OrderDate.Value.Year > 1996
select o
);

db.LoadOptions = lo;

var query =
from c in db.Customers
where ((c.City != null) && (c.City == "London" || c.City == "Bräcke"))
select c;


//db.Log = Console.Out;

var customers = query.ToList();

foreach (var c in customers)
{
Console.WriteLine(c.CompanyName);
Console.WriteLine("---------------------------------------------------");
Console.WriteLine("---------------------------------------------------");
foreach (var o in c.Orders)
{
Console.WriteLine(
"{0} [{1}] ${2} total",
o.OrderDate.Value.ToShortDateString(),
o.Order_Details.Count(),
Convert.ToInt32(o.Order_Details.Sum(od => (od.Quantity * od.UnitPrice)))
);

foreach (var od in o.Order_Details)
{
Console.WriteLine(" - {0} [{1}]", od.Product.ProductName, od.Quantity);
}
Console.WriteLine("----");
}
Console.WriteLine();
}
}
}
}

// Thanks for reading this far =)
 
What could I do better? Or Different? OrElse?

Well. data-contexts are usually IDisposable, so I'd be "using" db.

Since we aren't passing the query out-of-scope (so we know the time
bounds) I probably wouldn't force ToList() upon it; just leave it
alone and enumerate on query. As a *general* rule, and unless I had
specific intent, I'd actually probably try to get all this back in a
single joined projection anyway; it would simplify the setup (no
LoadWith etc) and would reduce the traffic to the database - only the
columns in my projection would be returned. Of course, this might not
be possible/easy in this case since you have 3 levels of nested detail
(quite easy with 2 as long as you don't object to some duplication).

Marc
 
Marc Gravell said:
Well. data-contexts are usually IDisposable, so I'd be "using" db.

Hmm. As far as I know (which is very little and often wrong) the Dispose
does nothing.
Maybe just added to be sure for future changes.

A major feature with a DataContext is that you don't have to use a using
block. It manages connections for you. I hope I am right, as if a
DataContext needs to be disposed, I think a very important aspect of the
'connection-managager' are lost, and the beauty is gone.
Since we aren't passing the query out-of-scope (so we know the time
bounds) I probably wouldn't force ToList() upon it; just leave it
alone and enumerate on query.

Aight. I have this for clarity. But I must confess that I have done the same
in production code.
But I realize that getting a list and then iterating over it is twice the
work.

(Note to self: Be careful about overusing .ToList on large amounts of data)
As a *general* rule, and unless I had
specific intent, I'd actually probably try to get all this back in a
single joined projection anyway; it would simplify the setup (no
LoadWith etc) and would reduce the traffic to the database

Hmm. I think you are wrong on this one.
While the sql generated consists of like eight selects, it is sent in one
request and and the eight resultsets are sent back from sql server in one
response. As all the 'where' statements are pretty much the same, sql server
can optimize and not do the selects one-by-one. sql server will ceate a
bitmap over customers, but instead of dropping that bitmap, reuse it for the
orders. once it have the orders it already knows what orderitems that fits
the set already solved.
only the
columns in my projection would be returned. Of course, this might not
be possible/easy in this case since you have 3 levels of nested detail
(quite easy with 2 as long as you don't object to some duplication).

Well, doing a join would send a lot of redundant data from sql server over a
network. As far as I know, using 'data-shaping' a la linq with LoadWith is
much better. What I think is so great with linq is that you can stop
thinking of 'recordset', the old 2-dimensional excel-matrix style of data,
and instead think about hierarchies. I made a query that sported a few
thousand order_detalis, where they all referenced only two products. The
last query generated had sql deliver only two products, not two products
sported thousands of times.

I hope that someone who knows more on this will comment.

Also, it is amazing how fast you get into linq. When I first saw a query
using LoadOptions it looked like ancient greek to me. Especially as the
LoadWith comes before the main query, so you have to read it 'backwards'.
But after doing a few queries it starts to make sense. I can read a
linq-query with ease now. Much easier than any join-sql statement.

Thanks for your input Marc. Please give me more. =)
I read pretty much everything you post in here and you have my sincere
respect.

- Michael Starberg
 
Michael Starberg said:
Hmm. As far as I know (which is very little and often wrong) the Dispose
does nothing.
Maybe just added to be sure for future changes.

No, it stops the context being used again, including by existing
queries which are ready to execute. That can be a blessing or a
curse...
A major feature with a DataContext is that you don't have to use a using
block. It manages connections for you. I hope I am right, as if a
DataContext needs to be disposed, I think a very important aspect of the
'connection-managager' are lost, and the beauty is gone.

I mailed Matt Warren about this a little while ago. Here's the reply I
got:

<quote>
There are a few reasons we implemented IDispose

1) If application logic needs to hold onto an entity beyond when the
DataContext is expected to be used or valid you can enforce that
contract by calling Dispose. Deferred loaders in that entity will still
be referencing the DataContext and will try to use it if any code
attempts to navigate the deferred properties. These attempts will fail.
Dispose also forces the DataContext to dump its cache of materialized
entities so that a single cached entity will not accidentally keep
alive all entities materialized through that DataContext, which would
otherwise cause what appears to be a memory leak.

2) The logic that automatically closes the DataContext connection can
be tricked into leaving the connection open. The DataContext relies on
the application code enumerating all results of a query since getting
to the end of a resultset triggers the connection to close. If the
application uses IEnumerable's MoveNext method instead of a foreach
statement in C# or VB, you can exit the enumeration prematurely. If
your application experiences problems with connections not closing and
you suspect the automatic closing behavior is not working you can use
the Dispose pattern as a work around.
</quote>
 
It manages connections for you.
Fine; but part of connection management is telling it when you are done, so
it can clean up. To be honest, I don't know whether the Dispose() is a "nop"
or not - but to my mind, if it is IDisposable, and you "own" it (for want of
a better word), then you should be calling Dispose().
Re shaping - I agree in part, and perhaps the non-trivial nature of your
sample means that your current approach is the clearest (which is normally
the most important thing long-tem); but taking a simpler example (i.e.
from a single table) - if you select a projection such as new {item.Foo,
item.Bar}, then typically *only* those values are retrieved. However, if
you select "item", all the fields are selected into a full entity, of
which you then only use two... this is moer noticeable if you use
aggregates of course...

Marc
 
(the second paragraph was not a quote; read it as a post - just my reader
going mad; or maybe just me...)

Marc
 
Jon Skeet said:
Michael Starberg <[email protected]> wrote:
I mailed Matt Warren about this a little while ago. Here's the reply I got:
<quote>
There are a few reasons we implemented IDispose

1) If application logic needs to hold onto an entity beyond when the
DataContext is expected to be used or valid you can enforce that
contract by calling Dispose. Deferred loaders in that entity will still
be referencing the DataContext and will try to use it if any code
attempts to navigate the deferred properties. These attempts will fail.
Dispose also forces the DataContext to dump its cache of materialized
entities so that a single cached entity will not accidentally keep
alive all entities materialized through that DataContext, which would
otherwise cause what appears to be a memory leak.

Problem is bad code and missuse of a DataContext.
2) The logic that automatically closes the DataContext connection can
be tricked into leaving the connection open. The DataContext relies on
the application code enumerating all results of a query since getting
to the end of a resultset triggers the connection to close. If the
application uses IEnumerable's MoveNext method instead of a foreach
statement in C# or VB, you can exit the enumeration prematurely. If
your application experiences problems with connections not closing and
you suspect the automatic closing behavior is not working you can use
the Dispose pattern as a work around.

Problem is bad code and missuse of a DataContext.

So, used corectly, there should be no reason to Dispose a DataContext?
And Matt explains it so well. Sometimes disposing would be a good idea.

for (..;..;..)
{
item.MoveNext():
if (someCondition)
{
context.Dispose();
break:
}
}

But then again, I repeat that if you are using for and not foreach, then
most likely you are in a state of sin. Or a C-coder that just can't let go
and go with the flow. IEnumerable<T> is your friend! =)

(Also, Why I love Matt Warren is becuase if you ask him question, you surely
will get an answer)

- Michael Starberg
 
Marc Gravell said:
(the second paragraph was not a quote; read it as a post - just my reader
going mad; or maybe just me...)

Marc

We got it Marc. I could tell iy was youir post and not a <q />.
As for usenet, I am ashamed to confess to that I use OE.
Lame, I know. My Bad!

- Michael Starberg
 
So, used corectly, there should be no reason to Dispose a DataContext?
And Matt explains it so well. Sometimes disposing would be a good idea.

I would personally put it as:

o Normally you don't *need* to Dispose.
o Where you want to use a context, perform a query and then not
need the context again, using a "using" statement indicates that
you don't expect it to be used again.
o In some other cases, it can be a pain to work out when the context
will actually finish being used - in which case there's no point
in bending the design out of shape just to call Dispose.
 
Jon Skeet said:
I would personally put it as:

o Normally you don't *need* to Dispose.
o Where you want to use a context, perform a query and then not
need the context again, using a "using" statement indicates that
you don't expect it to be used again.
o In some other cases, it can be a pain to work out when the context
will actually finish being used - in which case there's no point
in bending the design out of shape just to call Dispose.

I totally agree, with the added thought that moving a DataContext between
methods is bad design, IMHO
In a method, I would not use using, nor code in a way that I do need it.

New Rule For Self: When I need to dispose of a DataContext, it is really
time for some refactoring.

- Michael Starberg
 
New Rule For Self: When I need to dispose of a DataContext, it is really
time for some refactoring.

In the example you have given, you have complete knowledge that the
data-context is done with; we have finished walking over the data. So
why not simply dispose it? I obviously can't force you...

Marc
 
Marc Gravell said:
In the example you have given, you have complete knowledge that the
data-context is done with; we have finished walking over the data. So
why not simply dispose it? I obviously can't force you...

Marc

Why call on a Dispose that does nothing?

- Michael Starberg
 
Michael Starberg said:
Why call on a Dispose that does nothing?

It doesn't do nothing. It prevents the context from being accidentally
reused when you don't intend it to be. This happens more easily than
you might expect, if one of the entities created by the context
"escapes".
 
Sometimes disposing would be a good idea.

If I have DLinq IEnumerable implementation:

public virtual IEnumerator<T> GetEnumerator() {
SqlConnection newConn = new SqlConnection( mySqlConnString);
newConn.Open();
using( newConn )
using( IDbCommand cmd = ExecuteSqlCommand(newConn, out rdr) )
using( rdr ) {
while(rdr.Read())
yield return rdr.GetString(0);
}
}


If I use this enumerator in for (or foreach) like

for (..;..;..)
{
item.MoveNext():
if (someCondition)
{
context.Dispose();
break:
}
}

will the local connnection newConn disposed automatically by
context.Dispose() ?

Andrus.
 
I totally agree, with the added thought that moving a DataContext between
methods is bad design, IMHO

I WinForms browse grid application using DataContexted per form may be
prefectly reasonable and very good idea.
DataContext contains query which can be re-executed.
There is no any other way to store queries without using datacontext.

Andrus.
 
Back
Top