LINQ to SQL Repository - Part 1

I needed wanted to create a repository to enable a disconnected LINQ to SQL data tier. I've also put together a cached version, which allows caching of the entities, and automatic updating of the items in the cache and database.

Below is the base repository.


public abstract class BaseRepository<TEntity> where TEntity : class, new() {
public event EventHandler<EntityEventArgs<TEntity>> Adding;
public event EventHandler<EntityEventArgs<TEntity>> Updated;
public event EventHandler<EntityEventArgs<TEntity>> Deleting;
public string ConnectionString { get; set; }

public SiteBuilderApplicationServices AppServices { get; set; }

protected BaseRepository()
: this(WebAppSettings.ConnectionString) {

}

internal BaseRepository(string connectionString) {
ConnectionString = connectionString;
}

protected virtual SBTWebDataContext DataContext {
get {
var db = new SBTWebDataContext(ConnectionString);
return db;
}
}

protected void LogException(Exception ex) {
//TODO: Log Exception
}

/// <summary>
/// Returns a list of entities from the data store.
/// </summary>
/// <returns>IEnumerable of TEntity</returns>
public virtual IEnumerable<TEntity> List() {
using (var db = DataContext) {
var table = db.GetTable<TEntity>();
return ApplySort(table.AsQueryable()).ToList();
}
}

/// <summary>
/// Gets a list of TEntity from the data store, using a filter.
/// </summary>
/// <param name="filter">An expression to use to query the data store</param>
/// <returns>IEnumerable of TEntity</returns>
public virtual IEnumerable<TEntity> Find(System.Linq.Expressions.Expression<Func<TEntity, bool>> filter) {
using (var db = DataContext) {
var table = db.GetTable<TEntity>();
return ApplySort(table.Where(filter)).ToList();
}
}

protected internal IQueryable<TEntity> Query(DataContext db, System.Linq.Expressions.Expression<Func<TEntity, bool>> filter) {
var table = db.GetTable<TEntity>();
return ApplySort(table.Where(filter));
}

/// <summary>
/// A helper method that is called after the Query method. Override this method in a derieved repository to apply default sorting to the repository.
/// </summary>
/// <param name="query">The query to sort</param>
/// <returns>A sorted IQueryable of <typeparamref name="TEntity"/></returns>
protected virtual IQueryable<TEntity> ApplySort(IQueryable<TEntity> query) {
return query;
}

/// <summary>
/// Gets an entity from the data store.
/// </summary>
/// <param name="id">The ID of the entity to get.</param>
/// <returns>TEntity</returns>
public virtual TEntity Get(System.Linq.Expressions.Expression<Func<TEntity, bool>> selector) {
using (var db = DataContext) {
var table = db.GetTable<TEntity>();
return table.Single(selector);
}
}

/// <summary>
/// Gets the first element matching the sequence.
/// </summary>
public virtual TEntity FirstOrDefault(System.Linq.Expressions.Expression<Func<TEntity, bool>> selector) {
try {
using (var db = DataContext) {
var table = db.GetTable<TEntity>();
return table.FirstOrDefault(selector);
}
} catch (Exception ex) {
LogException(ex);
throw;
}
}

/// <summary>
/// Checks if the ID is valid on the data store.
/// </summary>
/// <param name="id">The ID to check.</param>
/// <returns>Boolean</returns>
public virtual bool Exists(System.Linq.Expressions.Expression<Func<TEntity, bool>> selector) {
using (var db = DataContext) {
var table = db.GetTable<TEntity>();
return table.Where(selector).Count() == 1;
}
}

public virtual TEntity Update(TEntity source) {
try {
using (var db = DataContext) {
Table<TEntity> table = db.GetTable<TEntity>();
ParameterExpression tempParameterExpression;
string keyMethod = db.GetPrimaryKey<TEntity>();
var selector = Expression.Lambda<Func<TEntity, bool>>(Expression.Equal(Expression.Property(tempParameterExpression = Expression.Parameter(typeof(TEntity), "x"), keyMethod), Expression.Property(Expression.Constant(source), keyMethod)), new ParameterExpression[] { tempParameterExpression });

TEntity destination = table.Single(selector);
PrepareForUpdate(source, destination);
db.SubmitChanges();
return destination;
}
} catch (Exception ex) {
LogException(ex);
throw;
}
}

/// <summary>
/// Adds an entity to the data store.
/// </summary>
/// <param name="entity">The entity to add</param>
/// <returns>TEntity</returns>
public virtual TEntity Add(TEntity entity) {
EntityEventArgs<TEntity> args = new EntityEventArgs<TEntity>(entity);
OnAdding(args);
if (!args.Cancel) {
using (var db = DataContext) {
var table = db.GetTable<TEntity>();
table.InsertOnSubmit(entity);
db.SubmitChanges();
return entity;
}
} else {
return null;
}
}

/// <summary>
/// Deletes an entity from the data store.
/// </summary>
/// <param name="id">The ID of the entity to delete</param>
/// <returns></returns>
public virtual bool Delete(System.Linq.Expressions.Expression<Func<TEntity, bool>> selector) {
bool deleted = false;
using (var db = DataContext) {
var table = db.GetTable<TEntity>();
var entity = table.FirstOrDefault(selector);
if (entity != null) {
EntityEventArgs<TEntity> args = new EntityEventArgs<TEntity>(entity);
OnDeleting(args);
if (!args.Cancel) {
table.DeleteOnSubmit(entity);
db.SubmitChanges();
deleted = true;
}
}
}
return deleted;
}

public virtual bool DeleteAll(System.Linq.Expressions.Expression<Func<TEntity, bool>> selector) {
bool deleted = false;
using (var db = DataContext) {
var table = db.GetTable<TEntity>();
var entities = table.Where(selector);
entities.ForEach(x => OnDeleting(new EntityEventArgs<TEntity>(x)));
table.DeleteAllOnSubmit(entities);
db.SubmitChanges();
deleted = true;
}
return deleted;
}

protected void PrepareForUpdate(TEntity source, TEntity destination) {
var pkProp = (from member in DataContext.Mapping.GetMetaType(typeof(TEntity)).DataMembers
select member).ToList();

Type entType = typeof(TEntity);
PropertyInfo[] properties = entType.GetProperties().Where(x => x.CanWrite).ToArray();
for (int i = 0; i < properties.Length; i++) {
int ti = i;
var metaDataInfo = pkProp.FirstOrDefault(x => x.Name == properties[ti].Name);
if (metaDataInfo != null) {
bool noprocess = metaDataInfo.IsAssociation | metaDataInfo.IsDbGenerated;

if (!noprocess) {
object value = entType.GetProperty(properties[i].Name).GetValue(source, null);
properties[i].SetValue(destination, value, null);
}
}
}
}

protected virtual void OnAdding(EntityEventArgs<TEntity> e) {
if (Adding != null) {
Adding(this, e);
}
}

protected virtual void OnUpdated(EntityEventArgs<TEntity> e) {
if (Updated != null) {
Updated(this, e);
}
}

protected virtual void OnDeleting(EntityEventArgs<TEntity> e) {
if (Deleting != null) {
Deleting(this, e);
}
}
}

Silverlight

It's been a long time since I have posted anything new, I've been too busy studying for the MCPD and learning the new features of .NET 3.5 and Silverlight.

I'm going to be posting a bunch of examples I have created with silverlight and .NET 3.5.

The snippet below is a nice javascript object which will move objects around your canvas allowing you to drag and drag and throw.


// File: Movement.js
PointMap.Movement = function(s, noderef) {
this.noderef = noderef;
this.time_to_move=0.6;
var plugin = s;
this.main = s.findName("Page")
this.loader_bar=200;
this.stage_wid=800;
this.friction=0.93;
this.init_opacity=0.95;
this.img_wid=200;
this.scroll_amount=10;
this.time_to_move=500; //in ms
this.cur_img=0;
this.num_images_loaded=0;
this.prev_wid=0;
this._root_xmouse=0;
this._root_ymouse=0;
this.scaleUpInt=0;
this.mouseCheckInt=0;
this.cur_node0X=0;
this.cur_node0Y=0;
this.img_scaling=false;
this.point1X = 0;
this.point1Y = 0;
this.point2X = 0;
this.point2Y = 0;
this.start_time = 0;
this.main_mouse_down = false;
this.p3X = 0;
this.p3Y = 0;
this.start_scale_time = 0;
setInterval(Silverlight.createDelegate(this, this.node0Tracker),10)
}
PointMap.Movement.prototype =
{
mainDown : function(s,e) {
var pt = e.getPosition(null);
clearInterval(this.scaleUpInt)
this.point1X=pt.X
this.point1Y=pt.Y
this.start_time=new Date().getTime()
var node0Ref=s.findName(this.noderef)
this.main_mouse_down=true
this.cur_node0X=node0Ref["Canvas.Left"]
this.cur_node0Y = node0Ref["Canvas.Top"];
s.captureMouse()
},
mainUp: function (s,e) {
var pt = e.getPosition(null);
this.main_mouse_down=false
this.point2X=pt.X
this.point2Y=pt.Y
//alert(point2Y)
this.chkX=Math.abs(this.point2X-this.point1X)
this.chkY=Math.abs(this.point2Y-this.point1Y)
//alert([chkX,chkY])
if(this.chkY<600 && this.chkX<600) {
this.end_time=new Date().getTime()
var elapsed=this.end_time-this.start_time
//alert(elapsed)
if(elapsed<220) {
this.p3Y=this.point2Y-this.point1Y
this.p3X=this.point2X-this.point1X
}
}
s.releaseMouseCapture()
},
mainMouseMove : function (s, e) {
var pt = e.getPosition(null);
this._root_xmouse = pt.x
this._root_ymouse = pt.y
},
node0Tracker : function() {
var check_zeroX = Math.abs(this.p3X)
this.end=0.7
var node0Ref=this.main.findName(this.noderef)
this.diff=this.prev_wid-this.stage_wid
if(this.main_mouse_down) {
var newleft = this.cur_node0X-(this.point1X-this._root_xmouse);
var newtop = this.cur_node0Y-(this.point1Y-this._root_ymouse);
if (newleft < 500) {
node0Ref["Canvas.Left"]=newleft;

}
if (newtop < 500) {
node0Ref["Canvas.Top"]=newtop;

}
this.lineTracker(node0Ref);
}
if(check_zeroX>this.end) {
if(!this.main_mouse_down) {
var newX = node0Ref["Canvas.Left"]+this.p3X/3.2;
var newY = node0Ref["Canvas.Top"]+=this.p3Y/3.2;
if (newX < 500){
node0Ref["Canvas.Left"]=newX;

}
if (newY<500) {
node0Ref["Canvas.Top"]=newY;

}
this.p3X*=this.friction
this.p3Y*=this.friction
this.lineTracker(node0Ref);
}
else {
this.p3X=0
this.p3Y=0
}
}
},
mouseChecker : function (s,me) {
if(this.img_mouse_down) {
s["Canvas.Left"]=this._root_xmouse-this.img_offset_x
s["Canvas.Top"]=this._root_ymouse-this.img_offset_y
}
}
}
}

Sharing Master Pages Accross Multipile IIS Applications

I've been searching for a way to share master pages accross multipile IIS applicaitons for a long time. There are several options out there from using the VirtualPageProvider to creating your Master Page in the GAC. I've not had the time to look into any of these options.

What I did find was a very easy solution. Use Virtual Directories.

All you have to do is:

Create a shared location on your web server where the master pages will reside. Dump your shared master page in there.

Add a new virtual directory to your web application via IIS. You can now reference the new virtual directory as though the master page is inside of your web application. Changes made to the global master page will result in the changes being made accrosss the entire set of web projects that you have configured with the master page in the virtual folder.

Easy, quick and works with out much hassle. Just keep in mind that paths are still referenced agianst the content page that is using that master page, so if you have shared images, be sure to set these up in the master page with links that will be able to figure out where the images are regardless of the location of the content page.

Visual Studio Power Toys

The Pack Installer is a tool that will let you see the latest Power Toys for Visual Studio, and other great developer oriented tools. It allows you to easily mark any tool or set of tools for download and installation, and streamline the installation process.

http://www.codeplex.com/PackInstaller

This tool will allow you to install hundreds of code snippets, sample projects as well as some very useful Visual Studio add on's.

A-Z Index - Very Cool

Here is a very cool A-Z Index.

http://www.humboldt.edu/

How it was done:

http://interllectual.com/coffee/creating-an-a-z-index-with-textpattern

Tags: ,

WeekDay part of a DateDiff SQL Statement

I've recently been working on a project where I will need to get the durration of a task using the start date, and the end date and the current date. In order to do this in "real" working time I've found this function to allow you to get the number of days between two dates, not including weekends.


CREATE FUNCTION [dbo].[DatePartWorkDays]
/***************************************************************************************
Purpose:
1. Given any valid start date and end date, this function will calculate and return
the number of workdays (Mon - Fri).
2. Given only a valid start date (end date has DEFAULT in it), this function will
return a 1 if the start date is a weekday and a 0 if not a weekday.
Usage:
1. MASTER.dbo.fn_WorkDays(@StartDate,@EndDate)
2. MASTER.dbo.fn_WorkDays(@StartDate,DEFAULT) --Always returns 1 or 0
3. MASTER.dbo.fn_WorkDays(@EndDate,@StartDate)
4. MASTER.dbo.fn_WorkDays(@StartDate,@StartDate) --Always returns 1 or 0
5. MASTER.dbo.fn_WorkDays(@EndDate,@EndDate) --Always returns 1 or 0
Notes:
1. Holidays are NOT considered.
2. Because of the way SQL Server calculates weeks and named days of the week, no
special consideration for the value of DATEFIRST is given. In other words, it
doesn't matter what DATEFIRST is set to for this function.
3. If the input dates are in the incorrect order, they will be reversed prior to any
calculations.
4. Only whole days are considered. Times are NOT used.
5. The number of workdays INCLUDES both dates
6. Inputs may be literal representations of dates, datetime datatypes, numbers that
represent the number of days since 1/1/1900 00:00:00.000, or anything else that can
be implicitly converted to or already is a datetime datatype.
7. Undocumented: The DATEPART(dw,date) does not actually count weeks... It counts the
transition to a Sunday regardless of the DATEFIRST setting. In essence, it counts
only whole weekends in any given date range.
8. This UDF does NOT create a tally table or sequence table to operate. Not only is
it set based, it is truly "tableless".
Error Indications:
1. If either the @StartDate or the @EndDate parameter is an invalid date, the
following error is returned...
"Server: Msg 242, Level 16, State 3, Line 3
The conversion of a char data type to a datetime data type resulted in an
out-of-range datetime value."
2. If either the @StartDate or the @EndDate parameter is a string not resembling a
date, the following error is returned...
"Server: Msg 241, Level 16, State 1, Line 3
Syntax error converting datetime from character string."
3. If only one parameter is passed, the following error is returned...
"Server: Msg 313, Level 16, State 2, Line 3
An insufficient number of arguments were supplied for the procedure or
function MASTER.dbo.fn_WorkDays."
Revisions:
Rev 00 - 12/12/2004 - Jeff Moden - Initial creation and test.
Rev 01 - 12/12/2004 - Jeff Moden - Load test, cleanup, document, release.
Rev 02 - 12/26/2004 - Jeff Moden - Return NULL if @StartDate is NULL or DEFAULT and
modify to be insensitive to DATEFIRST settings.
***************************************************************************************/
--======================================================================================
-- Presets
--======================================================================================
--===== Define the input parameters (ok if reversed by mistake)
(
@StartDate DATETIME,
@EndDate DATETIME = NULL --@EndDate replaced by @StartDate when DEFAULTed
)
--===== Define the output data type
RETURNS INT
AS
--======================================================================================
-- Calculate the RETURN of the function
--======================================================================================
BEGIN
--===== Declare local variables
--Temporarily holds @EndDate during date reversal
DECLARE @Swap DATETIME
--===== If the Start Date is null, return a NULL and exit
IF @StartDate IS NULL
RETURN NULL
--===== If the End Date is null, populate with Start Date value
-- so will have two dates (required by DATEDIFF below)
IF @EndDate IS NULL
SELECT @EndDate = @StartDate
--===== Strip the time element from both dates (just to be safe) by converting
-- to whole days and back to a date. Usually faster than CONVERT.
-- 0 is a date (01/01/1900 00:00:00.000)
SELECT @StartDate = DATEADD(dd,DATEDIFF(dd,0,@StartDate),0),
@EndDate = DATEADD(dd,DATEDIFF(dd,0,@EndDate) ,0)
--===== If the inputs are in the wrong order, reverse them
IF @StartDate > @EndDate
SELECT @Swap = @EndDate,
@EndDate = @StartDate,
@StartDate = @Swap
--===== Calculate and return the number of workdays using the
-- input parameters. This is the meat of the function.
-- This is really just one formula with a couple of parts
-- that are listed on separate lines for documentation
-- purposes.
RETURN (
SELECT
--Start with total number of days including weekends
(DATEDIFF(dd,@StartDate,@EndDate)+1)
--Subtact 2 days for each full weekend
-(DATEDIFF(wk,@StartDate,@EndDate)*2)
--If StartDate is a Sunday, Subtract 1
-(CASE WHEN DATENAME(dw,@StartDate) = 'Sunday'
THEN 1
ELSE 0
END)
--If EndDate is a Saturday, Subtract 1
-(CASE WHEN DATENAME(dw,@EndDate) = 'Saturday'
THEN 1
ELSE 0
END)
)
END

Tags: , , , ,

11 Tips for Time Management in a Web Worker World

Nice list for managing your time, directed at a Web Worker. If you’re like me, then some days when you have 1000 tasks to do and you get them all done, you have trouble figuring out exactly what you have accomplished. Here’s some tips for fixing that.

http://webworkerdaily.com/2006/12/11/11-tips-for-time-management-in-a-web-worker-world/