TARIQ SALAH

My interests are in: - Microsoft Visual Studio .Net - Microsoft SQL Server. - Delphi. - ERP. - DNS, SOA, SAS, ... etc.

Wednesday, October 13, 2010

Wednesday, March 19, 2008

Transparent Windows Forms Controls

Transparent Windows Forms Controls: "Creating transparent Windows Forms controls.

The transparency feature of the Windows Forms control leaves much to be desired and is a blatant fudge. The control is not really transparent, it just pretends to be by looking at the background of it's parent control and copying the appropriate portion of the image or background onto it's own surface during the OnPaintBackground method.

This means that a 'transparent' control placed on top of another on the same parent will in fact obscure the other child controls. Figure 1 shows this effect in action.


Figure1: A supposedly transparent Panel

The panel control to the right of the form obscures the PictureBox control and shows only the background of the parent.

In order to make a truly transparent control we need to do a couple of things. Firstly, it's necessary to change the behaviour of the window by giving it a WS_EX_TRANSPARENT style. This is accomplished by overriding the CreateParams property so that the correct window style is included when the control is instantiated. The listing below shows this property override.

protected override CreateParams CreateParams
{
get
{
CreateParams cp=base.CreateParams;
cp.ExStyle|=0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}

The second thing we need to do is to invalidate the parent of the control, not the control itself, whenever we need to update the graphics. This ensures that whatever is behind the control gets painted before we need to do our own graphics output. To do this, a routine such as that shown in the following listing is needed.

protected void InvalidateEx()
{
if(Parent==null)
return;
Rectangle rc"

pinvoke.net: WindowsMessages (Enums)

pinvoke.net: WindowsMessages (Enums)

public enum WindowsMessages : uint
{
WM_ACTIVATE = 0x6,
WM_ACTIVATEAPP = 0x1C,
WM_AFXFIRST = 0x360,
WM_AFXLAST = 0x37F,
WM_APP = 0x8000,
WM_ASKCBFORMATNAME = 0x30C,
WM_CANCELJOURNAL = 0x4B,
WM_CANCELMODE = 0x1F,
WM_CAPTURECHANGED = 0x215,
WM_CHANGECBCHAIN = 0x30D,
WM_CHAR = 0x102,
WM_CHARTOITEM = 0x2F,
WM_CHILDACTIVATE = 0x22,
WM_CLEAR = 0x303,
WM_CLOSE = 0x10,
WM_COMMAND = 0x111,
WM_COMPACTING = 0x41,
WM_COMPAREITEM = 0x39,
WM_CONTEXTMENU = 0x7B,
WM_COPY = 0x301,
WM_COPYDATA = 0x4A,
WM_CREATE = 0x1,
WM_CTLCOLORBTN = 0x135,
WM_CTLCOLORDLG = 0x136,
WM_CTLCOLOREDIT = 0x133,
WM_CTLCOLORLISTBOX = 0x134,
WM_CTLCOLORMSGBOX = 0x132,
WM_CTLCOLORSCROLLBAR = 0x137,
WM_CTLCOLORSTATIC = 0x138,
WM_CUT = 0x300,
WM_DEADCHAR = 0x103,
WM_DELETEITEM = 0x2D,
WM_DESTROY = 0x2,
WM_DESTROYCLIPBOARD = 0x307,
WM_DEVICECHANGE = 0x219,
WM_DEVMODECHANGE = 0x1B,
WM_DISPLAYCHANGE = 0x7E,
WM_DRAWCLIPBOARD = 0x308,
WM_DRAWITEM = 0x2B,
WM_DROPFILES = 0x233,
WM_ENABLE = 0xA,
WM_ENDSESSION = 0x16,
WM_ENTERIDLE = 0x121,
WM_ENTERMENULOOP = 0x211,
WM_ENTERSIZEMOVE = 0x231,
WM_ERASEBKGND = 0x14,
WM_EXITMENULOOP = 0x212,
WM_EXITSIZEMOVE = 0x232,
WM_FONTCHANGE = 0x1D,
WM_GETDLGCODE = 0x87,
WM_GETFONT = 0x31,
WM_GETHOTKEY = 0x33,
WM_GETICON = 0x7F,
WM_GETMINMAXINFO = 0x24,
WM_GETOBJECT = 0x3D,
WM_GETSYSMENU = 0x313,
WM_GETTEXT = 0xD,
WM_GETTEXTLENGTH = 0xE,
WM_HANDHELDFIRST = 0x358,
WM_HANDHELDLAST = 0x35F,
WM_HELP = 0x53,
WM_HOTKEY = 0x312,
WM_HSCROLL = 0x114,
WM_HSCROLLCLIPBOARD = 0x30E,
WM_ICONERASEBKGND = 0x27,
WM_IME_CHAR = 0x286,
WM_IME_COMPOSITION = 0x10F,
WM_IME_COMPOSITIONFULL = 0x284,
WM_IME_CONTROL = 0x283,
WM_IME_ENDCOMPOSITION = 0x10E,
WM_IME_KEYDOWN = 0x290,
WM_IME_KEYLAST = 0x10F,
WM_IME_KEYUP = 0x291,
WM_IME_NOTIFY = 0x282,
WM_IME_REQUEST = 0x288,
WM_IME_SELECT = 0x285,
WM_IME_SETCONTEXT = 0x281,
WM_IME_STARTCOMPOSITION = 0x10D,
WM_INITDIALOG = 0x110,
WM_INITMENU = 0x116,
WM_INITMENUPOPUP = 0x117,
WM_INPUT = 0x00FF,
WM_INPUTLANGCHANGE = 0x51,
WM_INPUTLANGCHANGEREQUEST = 0x50,
WM_KEYDOWN = 0x100,
WM_KEYFIRST = 0x100,
WM_KEYLAST = 0x108,
WM_KEYUP = 0x101,
WM_KILLFOCUS = 0x8,
WM_LBUTTONDBLCLK = 0x203,
WM_LBUTTONDOWN = 0x201,
WM_LBUTTONUP = 0x202,
WM_MBUTTONDBLCLK = 0x209,
WM_MBUTTONDOWN = 0x207,
WM_MBUTTONUP = 0x208,
WM_MDIACTIVATE = 0x222,
WM_MDICASCADE = 0x227,
WM_MDICREATE = 0x220,
WM_MDIDESTROY = 0x221,
WM_MDIGETACTIVE = 0x229,
WM_MDIICONARRANGE = 0x228,
WM_MDIMAXIMIZE = 0x225,
WM_MDINEXT = 0x224,
WM_MDIREFRESHMENU = 0x234,
WM_MDIRESTORE = 0x223,
WM_MDISETMENU = 0x230,
WM_MDITILE = 0x226,
WM_MEASUREITEM = 0x2C,
WM_MENUCHAR = 0x120,
WM_MENUCOMMAND = 0x126,
WM_MENUDRAG = 0x123,
WM_MENUGETOBJECT = 0x124,
WM_MENURBUTTONUP = 0x122,
WM_MENUSELECT = 0x11F,
WM_MOUSEACTIVATE = 0x21,
WM_MOUSEFIRST = 0x200,
WM_MOUSEHOVER = 0x2A1,
WM_MOUSELAST = 0x20A,
WM_MOUSELEAVE = 0x2A3,
WM_MOUSEMOVE = 0x200,
WM_MOUSEWHEEL = 0x20A,
WM_MOUSEHWHEEL = 0x20E,
WM_MOVE = 0x3,
WM_MOVING = 0x216,
WM_NCACTIVATE = 0x86,
WM_NCCALCSIZE = 0x83,
WM_NCCREATE = 0x81,
WM_NCDESTROY = 0x82,
WM_NCHITTEST = 0x84,
WM_NCLBUTTONDBLCLK = 0xA3,
WM_NCLBUTTONDOWN = 0xA1,
WM_NCLBUTTONUP = 0xA2,
WM_NCMBUTTONDBLCLK = 0xA9,
WM_NCMBUTTONDOWN = 0xA7,
WM_NCMBUTTONUP = 0xA8,
WM_NCMOUSEHOVER = 0x2A0,
WM_NCMOUSELEAVE = 0x2A2,
WM_NCMOUSEMOVE = 0xA0,
WM_NCPAINT = 0x85,
WM_NCRBUTTONDBLCLK = 0xA6,
WM_NCRBUTTONDOWN = 0xA4,
WM_NCRBUTTONUP = 0xA5,
WM_NEXTDLGCTL = 0x28,
WM_NEXTMENU = 0x213,
WM_NOTIFY = 0x4E,
WM_NOTIFYFORMAT = 0x55,
WM_NULL = 0x0,
WM_PAINT = 0xF,
WM_PAINTCLIPBOARD = 0x309,
WM_PAINTICON = 0x26,
WM_PALETTECHANGED = 0x311,
WM_PALETTEISCHANGING = 0x310,
WM_PARENTNOTIFY = 0x210,
WM_PASTE = 0x302,
WM_PENWINFIRST = 0x380,
WM_PENWINLAST = 0x38F,
WM_POWER = 0x48,
WM_PRINT = 0x317,
WM_PRINTCLIENT = 0x318,
WM_QUERYDRAGICON = 0x37,
WM_QUERYENDSESSION = 0x11,
WM_QUERYNEWPALETTE = 0x30F,
WM_QUERYOPEN = 0x13,
WM_QUERYUISTATE = 0x129,
WM_QUEUESYNC = 0x23,
WM_QUIT = 0x12,
WM_RBUTTONDBLCLK = 0x206,
WM_RBUTTONDOWN = 0x204,
WM_RBUTTONUP = 0x205,
WM_RENDERALLFORMATS = 0x306,
WM_RENDERFORMAT = 0x305,
WM_SETCURSOR = 0x20,
WM_SETFOCUS = 0x7,
WM_SETFONT = 0x30,
WM_SETHOTKEY = 0x32,
WM_SETICON = 0x80,
WM_SETREDRAW = 0xB,
WM_SETTEXT = 0xC,
WM_SETTINGCHANGE = 0x1A,
WM_SHOWWINDOW = 0x18,
WM_SIZE = 0x5,
WM_SIZECLIPBOARD = 0x30B,
WM_SIZING = 0x214,
WM_SPOOLERSTATUS = 0x2A,
WM_STYLECHANGED = 0x7D,
WM_STYLECHANGING = 0x7C,
WM_SYNCPAINT = 0x88,
WM_SYSCHAR = 0x106,
WM_SYSCOLORCHANGE = 0x15,
WM_SYSCOMMAND = 0x112,
WM_SYSDEADCHAR = 0x107,
WM_SYSKEYDOWN = 0x104,
WM_SYSKEYUP = 0x105,
WM_SYSTIMER = 0x118, // undocumented, see http://support.microsoft.com/?id=108938
WM_TCARD = 0x52,
WM_TIMECHANGE = 0x1E,
WM_TIMER = 0x113,
WM_UNDO = 0x304,
WM_UNINITMENUPOPUP = 0x125,
WM_USER = 0x400,
WM_USERCHANGED = 0x54,
WM_VKEYTOITEM = 0x2E,
WM_VSCROLL = 0x115,
WM_VSCROLLCLIPBOARD = 0x30A,
WM_WINDOWPOSCHANGED = 0x47,
WM_WINDOWPOSCHANGING = 0x46,
WM_WININICHANGE = 0x1A,
WM_XBUTTONDBLCLK = 0x20D,
WM_XBUTTONDOWN = 0x20B,
WM_XBUTTONUP = 0x20C
}

Friday, March 7, 2008

West Wind - Mr. Rich Strahl A simple Business Object wrapper for LINQ to SQL

Since two months, I go here in there in the internet to find a usefull starting for Linq to SQL in n-tier. After every round, I return the the most usefull project I've ever seen. The Entity Bags is ofcourse very usefull as well.

Mr. Rich Strahl has the original blog


I've been talking about how I'm using a small business object wrapper around
LINQ to SQL to provide better abstraction of the LINQ to SQL functionality and I
suppose the time's come to be bit more specific. This is going to be a long post
with a fair bit of code and a brief discussion of the whys and hows. So I want
to share my ideas of what I'm thinking here and an overview of what I've built
so far. It's not complete, but enough to work with to get a feel for it and I'm
using it successfully with a sample app I'm building for my ASP.NET Connections
session. Granted it's not a complicated app (a Time Trakker) but I have to admit
- especially given my original misgivings and skepticism towards LINQ to SQL -
it's working out pretty damn well.


I'm throwing this out now to get some thoughts and feedback and I'll post my
current code so any so inclined can play around with this, but keep in mind this
is rougher than stuff I usually put out publicly so it may take some twiddling.


So... where to start?


LINQ to SQL is essentially an OR/M tool that requires modeling your data
model to an object model. But while you are using objects, in essence LINQ to
SQL still is little more than a fancy Data Access Layer. It does not make a
business object layer or middle tier.


It seems these days the concept of business objects is getting buried
somewhat under the veil of technology. I was reminded of that the other day
while reading a post on Rocky's blog

regarding CSLA in comparison Enterprise Framework
. We are so absorbed by the
discussion of the technological choices that the business and logistical aspects
and separation of responsibility often just seems to get lost. It's rare these
days to hear people talking about business objects anymore - the focus is all on
the front end and the data access technology itself which is kinda sad, because
in my mind at least business objects really provide the biggest productivity and
stability boost in app development.


My goal with what I'm building is not to build an end all framework - I've
never been a fan of big frameworks, but ever since the dawn of time it seems
I've built myself a small set of business object wrappers that facilitate the
process of building data centric applications. And this is just one more
iteration of it. This is nowhere as complete as
CSlA or
Mere Mortals or some other framework
would be but it is also smaller and easier to work with. In time, I'm sure
bigger frameworks will integrate with LINQ to SQL or Entity Framework too, but
right now if you want to use this technology you have to roll your own. Again,
this is my vision for my needs and I'm simply sharing it here - I'm not trying
to get into a holy war on which approach is better <s>.


So, Business Objects in my mind serve several main purposes (at a minimum):


Logical Abstraction

They abstract the object model in such a way that all code related to
accessing the object model (in this case LINQ to SQL Entities) as well as any
other incidental data access in one place. While an object model may map
database tables and relationships 1 - 1 , the business layer can combine these
objects in any combination necessary to provide the logical high level
abstraction. The typical example here is an invoice object that controls Invoice
header, line items and customers for example. The invoice knows how to access
all of these components and its the business object that accesses the model not
the front end code. Front end code typically interacts with the business object
and receives result data (queries or data objects) or in the case of CRUD
operations entity objects to work with in the UI.


Code Abstraction

Just as important as the logical abstraction is the code abstraction in
that all code related to a business object ends up in one place. This may be a
single class or a set of classes, but there's a distinct place where are
'business logic' is addressed - always in one place. Never does the front end
code talk to the data directly (although with LINQ there's some blurring of this
line because it gets a little fuzzy of what 'data' means when you're dealing
with LINQ expressions) it only sees the results which are put together by the
business object. One huge advantage with this scenario is that you can easily
reuse business object code in different applications or even different parts of
your application. So if you build an ASP.NET page that access the business
object, you use the same business object you might use a in a Web Service or
even a Windows Forms application. This sort of code reuse simply cannot happen
if you stick any sort of data access code into the UI using DataSource controls
<g>.


CommonBusiness Object and Data Functionality

LINQ to SQL provides a pretty easy data access model through the LINQ
to SQL entity classes and it's pretty straight forward. A business layer built
ontop of LINQ to SQL can certainly take advantage of this functionality and
reduce the amount of code that needs to get written to provide the basic DAL
functionality (which in non-OR/M environments must be built separately). With
LINQ to SQL a business layer can leverage that functionality directly and even
provide much additional value for data retrieval operations by
returning results as
LINQ queries as I mentioned yesterday
.


But there are also many things missing that a business layer needs.
Validation for example, is not really something that you can or should handle on
the entity model itself. LINQ to SQL does provide OnValidate methods for each
mapped property it creates, but this sort of mechanism is very difficult to
consolidate in a unified validation strategy. So at minmium there should be a
ValidationErrors facility that can be checked and used to ask a business object
to validation itself with a simple Validate() method perhaps.


Loading and Saving too should be simpler for front end code than what LINQ to
SQL provides. Realistically UI code should have to know very little about the
data it needs to access and certainly for simple tasks like retrieving an entity
instance you should just be able to provide a key or potentially call
specialized bus object methods that return an entity. This translates into
standard Load(pk) and Save() methods that know what to do without any further
setup.


Given that LINQ to SQL has some 'issues' with disconnected operation that
model should also be abstracted. In my simple framework I have an option flag on
the business object that allows either connected or disconnected operation. By
default the same connected mode that LINQ to SQL uses is used where a
DataContext instance jacked to the business object holds the context's change
state. Any Save Operation then simply calls submit changes. In disconnected mode
the business object creates a new context for each operation - to feed entities
out and to save them back. The connected mode is more efficient, but if you need
to disconnect it's easily done or can be overridden for the default behavior
altogether when the object is created.


Finally LINQ to SQL provides essentially DAL functionality - but it doesn't
provide much in the way of core ADO.NET operations which in some situations
might be handy. There's no direct support for executing a SQL command and
returning a DataReader or DataTable for example. There's not a lot of need for
this functionality, but I've found that at times it's damn handy to be able to
return data in otherways. So there's an extension of the DataContext that
provides the abillity to easily create commands, parameters, and run database
queries and commands more easily.


There's also a Converter class on the business object that can take a query
(generated through this same business object's DataContext) and convert it into
a resultset other than an EntityList. This it turns out is pretty important in
that data binding to good old ADO.NET types is much more efficient than binding
to entity lists which require Reflection to bind each data item.


A simple Business Framework for LINQ to SQL

So without much further ado here's what I've built so far. Keep in mind
that this is just a start although I'm finding that what I have so far is quite
adequate to work with.


Here's a a class diagram (with the hacked markup since VS can't deal with the
generic associations) that shows what the object model looks like:




wwBusinessObject


From a usability point of view compared to LINQ to SQL there's basically just
one extra level of indirection: The business object. So to use this model, you
create a new business object that inherits from wwBusinessObject<TEntity,
TContext> and you provide the generated DataContext and EntityType from your
LINQ to SQL model as generic parameters.


For example:



public class busEntry : wwBusinessObject<EntryEntity, TimeTrakkerContext>

 


A  DataContext is created for each business object and it manages its
own context. So two business object instances don't share the same context
state.


The TEntity generic parameter is used for providing a link to the 'main'
Entity that is associated with the business object. The business object may deal
with more entities internally but there's usually going to be a primary entity
that drives the BO. The primary entity is what is updated for the built in CRUD
methods, so when you call Load() you get an instance of the provided entity
type.


Note that the business object also has an internal Entity member which is
loaded by each of the load operations. I've always found this very convenient
because it makes it easy to pass around entity data in the context of a page
without having to create separate instances. Load() and NewEntity() will
automatically set the internal entity member, but also return the entity as
return value. The .Entity property is merely a convenience.


For CRUD operations the business object doesn't require any setup or
configuration code. Simply the above definition is enough to use code like the
following:



this.Entry = new busEntry();

 

if (this.Entry.Load(10) == null)  // load by pk

    return false;

 

this.Entry.Entity.Title = "My Entry";

 

if (!this.Entry.Save())

{

    this.SetError(this.Entry.ErrorMessage);

    return;

}

 

this.Entry.NewEntity();

this.Entity.Title = "New Entity";

this.Entity.Save();

 

// *** Still pointing at new entity

this.Entry.Entity.Title = "Something else";

 

// *** Delete the entity

if (this.Entry.Delete())

    this.SetError("Couldn't delete: " + this.Entry.ErrorMessage);

 


This may not seem like a big improvement over LINQ to SQL, but it's actually
a lot less code than you'd have to write even with plain LINQ to SQL code and it
handles a fair amount of details behind the scenes such as error handling.


Here the client code is not talking directly to the data model to retrieve or
save data - all it does is interact with the entity and then call the business
object 'to deal with it'. No LINQ syntax for any of this which is as it should
be IMHO. The UI doesn't talk to the data or even the model directly, only to the
entities.


Query methods in the business object (such as GetRecentEntries() for example)
generally are implemented as methods that return Query objects. Preferrably
you'd want to return strongly typed entities like this bus object method:



/// <summary>

/// Get open entries for a given user

/// </summary>

/// <param name="userPk"></param>

/// <returns></returns>

public IQueryable<EntryEntity> GetOpenEntries(int userPk)

{

    IQueryable<EntryEntity> q = 

        from e in this.Context.EntryEntities

        where !e.PunchedOut

        orderby e.TimeIn

        select e;

 

    // *** Add filter for User Pk - otherwise all open entries are returned

    if (userPk > 0)

        q = q.Where(e => e.UserPk == userPk);

 

    return q;

}


So that the front end code can optionally further filter the query. When a
strongly typed query is returned there are a lot of options to deal with the
data as shown in the following snippet:



 

protected override void OnInit(EventArgs e)

{

    base.OnInit(e);

 

    // *** Set up the base query

    IQueryable<EntryEntity> entries = this.entry.GetOpenEntries(this.TimeTrakkerMaster.UserPk);

 

    int count = entries.Count();

    if (count == 1)

    {                

        int? Pk = entries.Select(en => en.Pk).FirstOrDefault();

        if (Pk == null)

            Response.Redirect("~/Default.aspx");

 

        Response.Redirect("~/punchout.aspx?id=" + Pk.ToString());

    }

 

    // *** Assign the data source - note we can filter the data here!

    this.lstEntries.DataSource = this.entry.Converter.ToDataReader(
entries.Select(en => new { en.Pk, en.Title, en.TimeIn }));

 

    this.lstEntries.DataBind();

}


All the queries that actually hit the database are shown in bold. Notice that
there are 3 different queries that are run from the original query returned from
the business object, giving the front end code a ton of control of how to
present the data in the UI.


Notice also the entry.Converter.ToDataReader() call for databinding. This
isn't strictly necessary - you could directly bind the result of the query.
However, databinding to a DataReader() is 3-4 times faster than binding to an
entity list as Entity list binding requires use of Reflection for each data
item. The DataConverter provides an easy way to convert to DataReader, DataTable
(useful for paging, editing and still way faster binding than Entities) and
List. Another advantage of the Converter is that it fires any errors into the
business object so errors can be trapped more effectively.


Ok... so the CRUD code above is in connected mode. If you want to run CRUD
operations in disconnected this should work:


// *** You can also work on the entities disconnected

this.Entry.Options.TrackingMode = Westwind.BusinessFramework.TrackingModes.Disconnected;            

 

EntryEntity entry = this.Entry.Load(10);

entry.Title = "Updated Title";

this.Entry = null;   // kill business object

 

// *** Create a new one

this.Entry = new busEntry();

this.Entry.Options.TrackingMode = Westwind.BusinessFramework.TrackingModes.Disconnected;

this.Entry.Save(entry);  // update disconnected entity

To give you an idea of what this looks like in a Web page here's some code
handles displaying and then saving entity data for a new time entry:




protected override void OnInit(EventArgs e)

{

    base.OnInit(e);

 

    this.Proxy.TargetCallbackType = typeof(Callbacks);  // wwMethodCallback Ajax Callbacks handler

 

    if (this.Proxy.IsCallback)

        return;

 

    object projectQuery = Project.GetOpenProjects();

 

 

    this.lstProjects.DataSource = Project.Converter.ToDataReader(projectQuery);

    this.lstProjects.DataValueField = "Pk";

    this.lstProjects.DataTextField = "ProjectName";

    this.lstProjects.DataBind();

 

    object customerQuery = Customer.GetCustomerList();

 

    this.lstCustomers.DataSource = Customer.Converter.ToDataReader(customerQuery); 

    this.lstCustomers.DataTextField = "Company";

    this.lstCustomers.DataValueField = "Pk";

    this.lstCustomers.DataBind();

}



protected
override void OnLoad(EventArgs e)

{

    base.OnLoad(e);

 

    if (this.Proxy.IsCallback)

        return;

 

    this.TimeTrakkerMaster.SubTitle = "Punch In New Entry";

 

    if (this.Entry.NewEntity() == null)

    {

        this.ErrorDisplay.ShowError("Unable to load new Entry:<br/>" + this.Entry.ErrorMessage);

        return;

    }

 

    if (!this.IsPostBack)

    {

        // *** Get the User's last settings
// we need to load user here no association yet

        busUser user = TimeTrakkerFactory.GetUser();

        if (user.Load(this.TimeTrakkerMaster.UserPk) != null)

       {                    

            if ( user.Entity.LastCustomer > 0 )

                this.Entry.Entity.CustomerPk = user.Entity.LastCustomer;

            if (user.Entity.LastProject > 0)

                this.Entry.Entity.ProjectPk = user.Entity.LastProject;

        }

 

        // *** Now bind it

        this.DataBinder.DataBind();

    }

}

 

 

protected void btnPunchIn_Click(object sender, EventArgs e)

{

    // *** Start by unbinding the data from controls into Entity

    this.DataBinder.Unbind();

 

    // *** Manual fixup for the split date field

    DateTime PunchinTime = Entry.GetTimeFromStringValues(this.txtDateIn.Text, this.txtTimeIn.Text);

    if (PunchinTime <= App.MIN_DATE_VALUE)

    {

        this.DataBinder.BindingErrors.Add( new Westwind.Web.Controls.BindingError("Invalid date or time value", this.txtDateIn.ClientID));

        Entry.ValidationErrors.Add("Invalid date or time value", this.txtTimeIn.ClientID);

    }

    Entry.Entity.TimeIn = PunchinTime;

 

    // *** Validate for binding errors - and error out if we have any

    if (this.DataBinder.BindingErrors.Count > 0)

    {

        this.ErrorDisplay.ShowError(this.DataBinder.BindingErrors.ToHtml(), "Please correct the following:");

        return;

    }

 

    // *** Have to make sure we associate a user with this entry

    Entry.Entity.UserPk = this.TimeTrakkerMaster.UserPk;

 

 

    // *** Validate business rules

    if (!this.Entry.Validate())

    {

        foreach (ValidationError error in this.Entry.ValidationErrors)

        {                    

            this.DataBinder.AddBindingError(error.Message,error.ControlID);

        }

        this.ErrorDisplay.ShowError(this.DataBinder.BindingErrors.ToHtml(), "Please correct the following:");

        return;

    }

 

 

    // *** Finally save the entity

    if (!this.Entry.Save())

        this.ErrorDisplay.ShowError("Couldn't save entry:<br/>" +

                                    this.Entry.ErrorMessage);

    else

    {

        this.ErrorDisplay.ShowMessage("Entry saved.");

        Response.AppendHeader("Refresh", "2;default.aspx");

 

        // *** Remember last settings for Project and Customer for the user

        // *** NOTE: Entry.Entity.User is not available here because it's a NEW record

        //           so we explicitly load and save settings

        busUser User = TimeTrakkerFactory.GetUser();

        User.SaveUserPreferences(Entry.Entity.UserPk, Entry.Entity.CustomerPk, Entry.Entity.ProjectPk);

    }

}


This is one of the simpler examples that deals mostly with a single business
object but it should give you an idea of how things work. I mentioned the query
functionality yesterday.


The business object's typical implementation will likely provide:


Query Result Methods

Methods that return data that is used in the front end. These are methods like
GetOpenProjects() or GetCustomerList() that generally take input parameters and
then create Queries that get returned as a result. Remember queries are not
executed until enumerated so effectively no SQL access occurs until databinding
happens.


Overridden CRUD operations

This is actually quite common: You'll want to set default values or
perform other operations on new entities, set default values (say an Updated
column) on or before saving. This can also mean speciaty methods that basically
overload CRUD operations. For example in Time Trakker I have things like PunchIn
and PunchOut that are essentially overloads of the Save() method that assign
specific values first. For my user object I have AuthenticateAndLoad(string
username, string password) which is essentially an overloaded Load() method.




Convenience Methods

Often you also have convenience methods that format data a certain way
or perform special taks in batch on the entity object for example. Or you may
have special operations that run updates against the database. Maybe a
'UpdateTimeTotals()' that ensures that all data in the tables are properly
calculated (which incidentally would be a non-LINQ operation using just a
command object).


Again to give you an idea of what this looks like here's my business object
for the Entry object:



 

    /// <summary>

    /// Business object related a time entry.

    /// </summary>

    public class busEntry : wwBusinessObject<EntryEntity, TimeTrakkerContext>

    {

        /// <summary>

        /// Get open entries for a given user

        /// </summary>

        /// <param name="userPk"></param>

        /// <returns></returns>

        public IQueryable<EntryEntity> GetOpenEntries(int userPk)

        {

            IQueryable<EntryEntity> q = 

                from e in this.Context.EntryEntities

                where !e.PunchedOut

                orderby e.TimeIn

                select e;

 

            // *** Add filter for User Pk - otherwise all open entries are returned

            if (userPk > 0)

                q = q.Where(e => e.UserPk == userPk);

 

            return q;

        }

 

        /// <summary>

        /// Get all open entries

        /// </summary>

        /// <returns></returns>

        public IQueryable<EntryEntity> GetOpenEntries()

        {

            return this.GetOpenEntries(-1);

        }

 

        /// <summary>

        /// Gets a list of recent entries 

        /// </summary>

        /// <param name="userPk"></param>

        /// <param name="Count"></param>

        /// <returns></returns>

        public IQueryable<EntryEntity> GetEntries(int userPk)

        {

            IQueryable<EntryEntity> q =

                from e in this.Context.EntryEntities                

                orderby e.TimeIn descending

                select e;

 

            return q;

        }

 

 

 

#region   overridden CRUD operation

        /// <summary>

        /// Sets default time value

        /// </summary>

        /// <returns></returns>

        public override EntryEntity NewEntity()

        {

            EntryEntity entry = base.NewEntity();

            if (entry == null)            

                return null;

 

            entry.TimeIn = TimeUtilities.RoundDateToMinuteInterval(DateTime.Now,

                                                                   App.Configuration.MinimumMinuteInterval,

                                                                   RoundingDirection.RoundUp);

            entry.TimeOut = App.MIN_DATE_VALUE;            

 

            return entry;

        }

 

        /// <summary>

        /// Fixes up times for Universal Time to the database

        /// </summary>

        /// <returns></returns>

        public override bool Save()

        {

            //if (this.Entity.TimeIn != null)

            //    this.Entity.TimeIn = this.Entity.TimeIn.ToUniversalTime();            

            //if (this.Entity.TimeOut != null)

            //    this.Entity.TimeOut = this.Entity.TimeOut.ToUniversalTime();

 

            return base.Save();

        }

 

        /// <summary>

        /// Fixes up times for local time from the database

        /// </summary>

        /// <param name="pk"></param>

        /// <returns></returns>

        public override EntryEntity Load(object pk)

        {

            if (base.Load(pk) == null)

                return null;

 

            //if (this.Entity.TimeIn != null)

            //    this.Entity.TimeIn = Entity.TimeIn.Value.ToLocalTime();

 

            //if (this.Entity.TimeOut != null)

            //    this.Entity.TimeOut = Entity.TimeOut.Value.ToLocalTime();

 

            return this.Entity;

        }

 

 

        /// <summary>

        /// Checks for empty title and time in values and associations for user, customer and project

        /// </summary>

        /// <returns></returns>

        public override bool Validate()

        {

            base.Validate();

 

            if (string.IsNullOrEmpty(this.Entity.Title))

                this.ValidationErrors.Add("The title is required","txtTitle");

 

            if (this.Entity.TimeIn <= App.MIN_DATE_VALUE)            

                this.ValidationErrors.Add("Time and/or date value is invalid","txtTimeIn");

 

            if (this.Entity.CustomerPk < 1)

                this.ValidationErrors.Add("A customer must be associated with this entry", "txtCustomerpk");

 

            if (this.Entity.ProjectPk < 1)

                this.ValidationErrors.Add("A project must be associated with this entry", "txtProjectPk");

 

            if (this.Entity.UserPk < 1)

                this.ValidationErrors.Add("A user must be associated with this entry", "txtUserPk");

 

            if (ValidationErrors.Count > 0)

                return false;

 

            return true;

        }

 

        /// <summary>

        /// punches out an individual entry and saves it

        /// </summary>

        /// <returns></returns>

        public bool PunchOut()

        {

            this.Entity.PunchedOut = true;

            if (this.Entity.TimeOut == null || this.Entity.TimeOut < this.Entity.TimeIn)

                this.Entity.TimeOut = DateTime.Now;

 

            return this.Save();

        }

 

        /// <summary>

        /// Punches in a new entry by setting punch in time

        /// </summary>

        /// <returns></returns>

        public bool PunchIn()

        {

            return this.PunchIn(null);

        }

 

        /// <summary>

        /// Punches in a new entry by setting punch in time

        /// </summary>

        /// <param name="entity"></param>

        /// <returns></returns>

        public bool PunchIn(EntryEntity entity)

        {

            this.Entity.PunchedOut = false;

 

            if ( this.Entity.TimeIn <= App.MIN_DATE_VALUE )

                this.Entity.TimeIn = DateTime.Now;

 

            if (Entity == null)

                return this.Save();

            else

                return this.Save(entity);

        }

 

        /// <summary>

        /// Punches out an individual entry and saves it

        /// </summary>

        /// <param name="entry"></param>

        /// <returns></returns>

        public bool PunchOut(EntryEntity entry)

        {

            entry.PunchedOut = true;

 

            if (entry.TimeOut == null || entry.TimeOut < entry.TimeIn)

                entry.TimeOut = DateTime.Now;

 

            this.CalculateItemTotals();

 

            return this.Save(entry);

        }

#endregion

 

        /// <summary>

        /// Utility function that converts a date time entry value from

        /// a date and time string to a DateTime value

        /// </summary>

        /// <param name="Date"></param>

        /// <param name="Time"></param>

        /// <returns></returns>

        public DateTime GetTimeFromStringValues(string Date, string Time)

        {

            DateTime val = App.MIN_DATE_VALUE;

            DateTime.TryParse(Date + " " + Time,out val);

 

            return val;

        }

 

        /// <summary>

        /// Calculates Item and Rate totals and sets it on the passed entry object

        /// </summary>

        public void CalculateItemTotals(EntryEntity Entry)

        {

            if (Entry == null)

                Entry = this.Entity;                       

 

            if (Entry.TimeIn == null || 

                Entry.TimeOut== null || 

                Entry.TimeOut < Entry.TimeIn)

                Entry.TotalHours = 0.00M;

            else if ( Entry.TimeOut > App.MIN_DATE_VALUE && 

                      Entry.TimeIn > App.MIN_DATE_VALUE)

                Entry.TotalHours = (decimal)Entry.TimeOut.Subtract(Entry.TimeIn).TotalHours;

 

            Entry.ItemTotal = Entry.TotalHours * Entry.Rate;

        }

 

 

        /// <summary>

        /// Calculates Item and Rate totals. This version works off the internal Entity object

        /// </summary>

        public void CalculateItemTotals()

        {

            this.CalculateItemTotals(null);

        }

 

        /// <summary>

        /// Adjusts the time values for rounding conditions

        /// </summary>

        public void RoundTimeValues()

        {

            if (this.Entity.TimeIn > App.MIN_DATE_VALUE)

                this.Entity.TimeIn = TimeUtilities.RoundDateToMinuteInterval(this.Entity.TimeIn,

                        App.Configuration.MinimumMinuteInterval,

                        RoundingDirection.RoundUp);

            if (this.Entity.TimeOut > App.MIN_DATE_VALUE)

                this.Entity.TimeOut = TimeUtilities.RoundDateToMinuteInterval(this.Entity.TimeOut,

                       App.Configuration.MinimumMinuteInterval,

                       RoundingDirection.RoundUp);

        }

 


 

    }


Most of the methods are pretty minimalistic - as you would expect in a simple
application like this. But it should give a pretty clear view of what typically
happens in the business object. The code essentially deals with the core data
manipulation, pre and post processing and of course generation of queries. The
code tends to break down into very clearly defined responsibilities that are
easy to test against. Further given the structure of the business object you
have a sort of template that you follow with most business objects. You
implement Validate() and probably one more Load() related methods and possibly
Save() if there's some special save syntax.


The business object uses LINQ to SQL for data access and passes out either
LINQ queries (IQueryable<T> for 'adjustable' results or IQueryable for fixed
non-mutable results) or returns individual entities for the CRUD methods. In
simple applications like the time tracking app I'm building nearly 90% of the
code deals with CRUD and convenience methods and just a few simple queries which
is quite common for transactional applications.


Most operations require relatively little amounts of code because of the high
level of abstraction that LINQ offers. From a pure code usability perspective
LINQ to SQL is making code cleaner and resulting in quite a bit less of it. So
far I haven't run into any of my own 'warning' points - mainly because I haven't
gotten there yet. I know I will have issues when I get to the Web Service
portion of things and already had to dodge one issue when using AJAX
serialization. But other than that so far so good.


So there you have it <g>. I'd be interested to hear thoughts.

LINQ to SQL and WCF - Sharing types, subverting the DataContext on the client side

I've found a starting ideas in this very helpfull chainais blog. He wrote about creating a DataContext in the client side with an empty connection string. This DataContext will be used in the first fitch of the data or after savings to keep the original entities. Then, before saves, he will compare the modified entitites witht the original one.
The original post is here.

Ok, so this whole post probably falls into the category of "nasty hack" but
it was something that I was playing with so I thought I'd share.


    I wrote a little

bit here previously
about working with LINQ to SQL in a disconnected sense
and wrote about how, if you wanted to manage concurrency in a reasonable way,
then you needed to be able to pass back to a middle-tier server the state of any
entities that you were deleting or updating in order that the server could see
which properties you'd actually updated and which (if any) of the set of
properties that are significant for optimistic concurrency might have been
changed by someone else since you took your data from the database.


    Now, in transferring data to a client from that
middle-tier server it seems to me that you can either;



  1. Share contract with the client. This is the
    "web services" way and divorces anything that happens on the client from
    what happens in the server. It has advantages (e.g. interoperability,
    versioning) but it's also quite hard to do because none of the clever bits
    that you have on the server side is available to you on the client side.

  2. Share types with the client. This is simpler
    to program against but might hurt you from the versioning point of view and
    almost certainly doesn't help you do interop.

    1. Note - the approach of sharing a DataSet between
      the client and the server falls into this category with the difference
      being that (for .NET) you're only sharing a single data type (i.e.
      DataSet) between the client and the
      server and that type isn't actually owned by you so maybe it's not so
      bad.




Whichever of these you go for, if you're returning objects that you get from
LINQ to SQL then you don't really have the DataSet (and its
ability to track changes) to help you and that means that someone, somewhere
(and a good candidate is the client) has to store "before" and "after" values
for the entities that you're manipulating on the client.


From here on in, I'm only talking about the shared types
approach.


If we were to go with the shared types approach
then it becomes interesting because (given the right flags) the LINQ to SQL bits
will give you data types (e.g. Customer, Order and so on from a
Northwind DB) that you can serialize straight to a client over WCF with little
effort.


So, down on the client side you've got a bunch of Customer
instances and these types are quite smart in that they already implement
INotifyPropertyChang[ing/ed]
and so can tell an interested party about
changes to themselves and this might well be a useful building block for you to
be able to build something client-side which allows you to relatively easily
determine which objects have been inserted, updated, deleted when it comes time
to submit them back to a middle-tier service.


I started to think about building some class that would sync up to these
property changed notifications for use on the client side and I kept coming back
to thinking "Hang, on - the DataContext can already do all this
stuff".


So...is it possible to make use of the DataContext client
side in order to do this "change notification stuff" ? It appears that it might
well be but this is where it all gets a bit hacky.


Here's where I ended up.


1) Built a class library project called DataTypes


Into this class library project I added the output of running
sqlmetal.exe /server:. /database:northwind /serialization:Unidirectional
/pluralize /code:northwind.cs
and that means that I've now got one
project with the data types that I need to serialize backwards and forwards to
my service code all contained in one place so that I can reference them from my
client and my service (shared types!).


2) Build a class library project called ServiceInterface


Into this, I just added the following WCF marked up interface;


namespace ServiceInterface
{
[ServiceContract]
public interface IServeCustomers
{
[OperationContract]
List<Customer> GetCustomersForCountry(string country);
[OperationContract]
void InsertCustomers(List<Customer> customers);
[OperationContract]
void DeleteCustomers(List<Customer> before,
List<Customer> after);
[OperationContract]
void UpdateCustomers(List<Customer> before,
List<Customer> after);
}
}

This references the DataTypes project and, again, can itself
be referenced by both my client and my service.


3) Built a WCF service to handle Customer instances
(console app)


This is very similar to what I did in a previous post. The service code looks
like this and references the ServiceInterface and
DataTypes
projects;


namespace Service
{
class Implementation : IServeCustomers
{
public List<Customer> GetCustomersForCountry(string country)
{
List<Customer> customers = null;
using (NorthwindDataContext ctx = new NorthwindDataContext())
{
ctx.ObjectTrackingEnabled = false;
customers =
(from c in ctx.Customers where c.Country == country select c).ToList();
}
return (customers);
}
public void InsertCustomers(List<Customer> customers)
{
using (NorthwindDataContext ctx = new NorthwindDataContext())
{
foreach (Customer c in customers)
{
ctx.Customers.Add(c);
}
ctx.SubmitChanges();
}
}
public void DeleteCustomers(List<Customer> before,
List<Customer> after)
{
using (NorthwindDataContext ctx = new NorthwindDataContext())
{
for (int i = 0; i < before.Count; i++)
{
ctx.Customers.Attach(after[i], before[i]);
ctx.Customers.Remove(after[i]);
}
ctx.SubmitChanges();
}
}
public void UpdateCustomers(List<Customer> before, List<Customer> after)
{
using (NorthwindDataContext ctx = new NorthwindDataContext())
{
for (int i = 0; i < before.Count; i++)
{
ctx.Customers.Attach(after[i], before[i]);
}
ctx.SubmitChanges();
}
}
}
}

 


And then I've got the hosting code;


    static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(Implementation));
host.Open();
Console.WriteLine("Listening...");
Console.ReadLine();
host.Close();
}

 


and the config file;


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="DataTypes.Properties.Settings.NorthwindConnectionString"
connectionString="Data Source=.;Initial Catalog=Northwind;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.serviceModel>
<services>
<service name="Service.Implementation">
<endpoint address="net.tcp://localhost:9091/customerService"
binding="netTcpBinding"
contract="ServiceInterface.IServeCustomers"
bindingConfiguration="myConfig"/>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="myConfig">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>

 


4) Built a client (console app)


This project references both the ServiceInterface and
DataTypes project. I hand-cranked a WCF proxy class as I
couldn't remember the tool option to do it;


  class ClientProxy : ClientBase<IServeCustomers>, IServeCustomers
{
public ClientProxy()
{
}
public ClientProxy(string config) : base(config)
{
}
public List<Customer> GetCustomersForCountry(string country)
{
return (base.Channel.GetCustomersForCountry(country));
}
public void InsertCustomers(List<Customer> customers)
{
base.Channel.InsertCustomers(customers);
}
public void DeleteCustomers(List<Customer> before,
List<Customer> after)
{
base.Channel.DeleteCustomers(before, after);
}
public void UpdateCustomers(List<Customer> before,
List<Customer> after)
{
base.Channel.UpdateCustomers(before, after);
}
}

 


and then I wrote this little class to make use of the underlying
DataContext
on the client side. Note - this is really a bit
subversive and possibly a bit evil but I thought I'd have a play with it.
Essentially, I'm trying to use the change-tracking capabilities of a DataContext
without ever trying to connect it to a DB which is not really what you're meant
to do (AFAIK). Anyway...


  public class ClientSideContext : IDisposable
{
public class StateEntries<T>
{
public List<T> Originals { get; set; }
public List<T> Current { get; set; }
}
public ClientSideContext()
{
ctx = new DataContext("", new AttributeMappingSource());
ctx.DeferredLoadingEnabled = false;
}
public void Attach<T>(T t) where T : class
{
ctx.GetTable<T>().Attach(t);
}
public void Remove<T>(T t) where T : class
{
ctx.GetTable<T>().Remove(t);
}
public void Add<T>(T t) where T : class
{
ctx.GetTable<T>().Add(t);
}
public List<T> GetInserted<T>() where T : class
{
return (GetChangeEntries<T>(ch => ch.AddedEntities));
}
public StateEntries<T> GetDeleted<T>() where T : class
{
return (GetStateEntries<T>(ch => ch.RemovedEntities));
}
private StateEntries<T> GetStateEntries<T>(
Func<ChangeSet, IEnumerable<Object>> entry) where T : class
{
List<T> current = GetChangeEntries<T>(entry);
List<T> originals = GetOriginals<T>(current);
return (new StateEntries<T>()
{
Originals = originals,
Current = current
});
}
public StateEntries<T> GetModified<T>() where T : class
{
return (GetStateEntries<T>(ch => ch.ModifiedEntities));
}
public void Dispose()
{
ctx.Dispose();
}
List<T> GetChangeEntries<T>(
Func<ChangeSet, IEnumerable<Object>> selectMember) where T : class
{
var query = from o in selectMember(ctx.GetChangeSet())
where ((o as T) != null)
select (T)o;
return (new List<T>(query));
}
List<T> GetOriginals<T>(List<T> current) where T : class
{
List<T> originals = new List<T>(
from c in current
select ctx.GetTable<T>().GetOriginalEntityState(c));
return (originals);
}
private DataContext ctx;
}

 


So, the idea here is that this class contains a DataContext
and allows you to Attach, Add, Remove instances to it. You can
then come back at a later point (presumably when you want to call back to your
middle-tier service) and you can do GetInserted(), GetModified(),
GetDeleted()
and it'll feed you the lists of objects (including
original values and current values where necessary) to pass back to that
middle-tier service.


Here's the client program code that I was playing with to try and exercise
this;


 


   static void Main(string[] args)
{
Console.WriteLine("Hit return to make call...");
Console.ReadLine();
ClientProxy proxy = new ClientProxy("clientConfig");
List<Customer> customers = proxy.GetCustomersForCountry("UK");
proxy.Close();
using (ClientSideContext ctx = new ClientSideContext())
{
foreach (Customer c in customers)
{
ctx.Attach(c);
// Simulate an update...
c.Country = "GB";
}
// Now insert...
ctx.Add(new Customer()
{
CustomerID = "Foo",
CompanyName = "Bar"
});
// Now delete...
ctx.Remove(customers[0]);
// Now, call back to service...
proxy = new ClientProxy("clientConfig");
proxy.InsertCustomers(ctx.GetInserted<Customer>());
var deleted = ctx.GetDeleted<Customer>();
proxy.DeleteCustomers(deleted.Originals, deleted.Current);
var modified = ctx.GetModified<Customer>();
proxy.UpdateCustomers(modified.Originals, modified.Current);
proxy.Close();
Console.ReadLine();
}
}

Naturally, this is hacky and perhaps it would have been better to write my
own class on the client-side rather than trying to bend the
DataContext
to do something like this but it seemed easier for what I
was playing with so I gave it a whirl and thought I'd share.


( No doubt, there are places where it'll go wrong :-) ).


Here's the config file for the client, just for completeness;


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint name="clientConfig"
address="net.tcp://localhost:9091/customerService"
binding="netTcpBinding"
contract="ServiceInterface.IServeCustomers"
bindingConfiguration="myConfig"/>
</client>
<bindings>
<netTcpBinding>
<binding name="myConfig">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>