How to Create a Custom LINQ to SQL LinqDataSource [Video]
This video, courtesy of Microsoft’s ASP.NET video series, provides an example of creating a custom LINQ to SQL LinqDataSource.
loading...
loading...
This video, courtesy of Microsoft’s ASP.NET video series, provides an example of creating a custom LINQ to SQL LinqDataSource.
This video, courtesy of Microsoft’s ASP.NET video series, provides an example of using the LINQ to SQL LinqDataSource.
This video, courtesy of Microsoft’s ASP.NET video series, provides an example of using LINQ to SQL to update a table in a database.
This video, courtesy of Microsoft’s ASP.NET video series, provides an example of using LINQ to SQL to query a database.
This video, courtesy of Microsoft’s ASP.NET video series, provides an example of using the LINQ to SQL data model for developing applications with LINQ to SQL.
This video, courtesy of Microsoft’s ASP.NET video series, provides an overview of LINQ to SQL, and how it can change the way you develop applications.
There are many little tips and tricks for LINQ to SQL which can make our lives easier, or improve the speed and efficiency of the generated code. Below are several tips and tricks I have found during my time as a LINQ developer.
*Note: Some of these tips are for querying directly from the table. While I'm a big advocate of always using stored procedures, there are circumstances where that may not be possible or desirable.*
LINQ to SQL lets you specify that a property is delay-loaded meaning
that it is not normally retrieved as part of normal query operations
against that entity. This is particularly useful for binary and large
text fields such as a photo property on an employee object that is
rarely used and would cause a large amount of memory to be consumed on
the client not to mention traffic between the SQL and application.
There are times however when you want all these binaries returned in
a single query, say for example returning all the photos for the
company photo intranet page:
var db = new NorthwindContext(); var loadOptions = new DataLoadOptions(); loadOptions.LoadWith<Employee>(e => e.Photo); db.LoadOptions = loadOptions;
There are times it is useful to be able to listen in to when these
events happen and perform your own logic, perhaps auditing or logging
for some scenarios. The easiest way to do this is to implement some
specially-named methods on your data context, perform your action and
then to dispatch the call back to LINQ to SQL.
The format of these specially-named methods is [Action][Entity] and
then you should pass back control to LINQ to SQL using
ExecuteDynamic[Action] where [Action] is either Insert, Update or
Delete. One example of such usage might be:
partial class NorthwindContext {
partial void InsertEmployee(Employee instance) {
instance.CreatedBy = CurrentUser;
instance.CreatedAt = DateTime.Now;
ExecuteDynamicInsert(instance);
}
partial void UpdateEmployee(Employee instance) {
AuditEmployeeOwnerChange(instance);
instance.LastModifiedAt = DateTime.Now;
ExecuteDynamicUpdate(instance);
}
partial void DeleteEmployee(Employee instance) {
AuditDelete(instance, CurrentUser);
ExecuteDynamicDelete(instance);
}
}
There are times when LINQ to SQL refuses to cook up the TSQL you
wanted either because it doesn’t support the feature or because it has
a different idea what makes an optimal query.
In either case the Translate method allows you to deliver your own
TSQL to LINQ to SQL to process as if it were its own with execution,
materialization and identity mapping still honored. For example:
var db = new PeopleContext();
if (db.Connection.State == System.Data.ConnectionState.Closed)
db.Connection.Open();
var cmd = db.GetCommand(db.Persons.Where(p => p.CountryID == 1));
cmd.CommandText = cmd.CommandText.Replace("[People] AS [t0]", "[People] AS [t0] WITH (NOLOCK)");
var results = db.Translate<Person>(cmd.ExecuteReader());
When working with stored procedures the LINQ to SQL designer and
SQLMetal tools need a way of figuring out what the return type will be.
In order to do this without actually running the stored procedure
itself they use the SET FMTONLY command set to ON so that SQL Server
will just parse the stored procedure instead.
Unfortunately this parsing does not extend to tdynamic SQL or
temporary tables so you must change the return type from the scalar
integer to one of the known entity types by hand. You could use the
following command at the start to let it run regardless given the
subsequent warning.
SET FMTONLY OFF
If your stored procedure can not safely handle being
called at any time with null parameters set the return type by hand
instead.
There are many reasons you might want to clone an entity – you may
want to create many similar ones, you could want to keep it around
longer than the DataContext it came from – whatever your reason
implementing a Clone method can be a pain but taking advantage of the
DataContractSerializer can make light work of this providing your DBML
is set to enable serialization.
public static T Clone<T>(T source) {
var dcs = new System.Runtime.Serialization.DataContractSerializer(typeof(T));
using (var ms = new System.IO.MemoryStream()) {
dcs.WriteObject(ms, source);
ms.Seek(0, System.IO.SeekOrigin.Begin);
return (T)dcs.ReadObject(ms);
}
}
And then to clone simply:
var source = myQuery.First();var cloned = Clone(source);
Be aware that this comes with a little overhead in the serialization and deserialization process.
LINQ OfType<> is a useful filtering function for returning items in a collection which match a specific type (or base type). An example might be for getting base items from a listbox of combobox.
An example without OfType:
ColorWrapper x = combo.Items.Where( obj => obj is ColorWrapper ).Cast< ColorWrapper >().FirstOrDefault(cw => cw.Color == value);
An example of LINQ OfType:
ColorWrapper y = combo.Items.OfType<ColorWrapper>().FirstOrDefault(cw => cw.Color == value);
I've been doing a lot of mult-threading work, recently, using the standard Thead class, the Worker Queue, and the new PLINQ (Parallel LINQ). The problem with most of the built-in generic collections (Queue<>, List<>, Dictionary<>, etc), is that they are not thread safe.
I created a library of thread safe collections which allow me to use the standard generic collection actions (foreach, LINQ, etc), while at the same time being thread safe.
The classes in this library inherit from the appropriate collection interface (IEnumerable, ICollection, etc). Each class also has all the functions and properties that it's original non-thread safe class has.
You can download a copy of the entire library, which includes support for a thread safe List<>, Dictionary<>, and Queue<>, here: Thread Safe Generic Collections
TQueue<> Example:
The first thing we need to do is create a container for the TQueue and a thread lock object. I generally prefer to use the ReaderWriterLockSlim because it is light weight and fast.
/// <summary>
/// The private q which holds the actual data
/// </summary>
private readonly Queue<T> m_Queue;
/// <summary>
/// Lock for the Q
/// </summary>
private readonly ReaderWriterLockSlim LockQ = new ReaderWriterLockSlim();
Just like a standard Queue, we have three overloads for the Initialization. These overloads allow an empty Queue to be created, a Queue with a specified capacity, or a Queue with an initial IENumerable collection to populate the Queue.
/// <summary>
/// Initializes the Queue
/// </summary>
public TQueue()
{
m_Queue = new Queue<T>();
}
/// <summary>
/// Initializes the Queue
/// </summary>
/// <param name="capacity">the initial number of elements the queue can contain</param>
public TQueue(int capacity)
{
m_Queue = new Queue<T>(capacity);
}
/// <summary>
/// Initializes the Queue
/// </summary>
/// <param name="collection">the collection whose members are copied to the Queue</param>
public TQueue(IEnumerable<T> collection)
{
m_Queue = new Queue<T>(collection);
}
This next function is probably the most important one. The GetEnumerator() is used during ForEach loops, and returns the next item in the collection. Following Microsoft's example of a thread-safe enumerator, we first get a copy of the current container Queue, then use this copy for iterating. You'll notice the use the Read lock before acquiring the container Queue copy.
/// <summary>
/// Returns an enumerator that enumerates through the collection
/// </summary>
public IEnumerator<T> GetEnumerator()
{
Queue<T> localQ;
// init enumerator
LockQ.EnterReadLock();
try
{
// create a copy of m_TList
localQ = new Queue<T>(m_Queue);
}
finally
{
LockQ.ExitReadLock();
}
// get the enumerator
foreach (T item in localQ)
yield return item;
}
A Queue must include an Enqueue and a Dequeue, used for adding and removing items from the collection. Just as in every other function, we're using the locks to protect our data access.
/// <summary>
/// Adds an item to the queue
/// </summary>
/// <param name="item">the item to add to the queue</param>
public void Enqueue(T item)
{
LockQ.EnterWriteLock();
try
{
m_Queue.Enqueue(item);
}
finally
{
LockQ.ExitWriteLock();
}
}
/// <summary>
/// Removes and returns the item in the beginning of the queue
/// </summary>
public T Dequeue()
{
LockQ.EnterWriteLock();
try
{
return m_Queue.Dequeue();
}
finally
{
LockQ.ExitWriteLock();
}
}
I found that many times I have a need to enqueue multiple items at once. This lead to the creation of the EnqueueAll functions. You'll notice the second overload is using the thread safe List (TList).
/// <summary>
/// Enqueues the list of items
/// </summary>
/// <param name="ItemsToQueue">list of items to enqueue</param>
public void EnqueueAll(IEnumerable<T> ItemsToQueue)
{
LockQ.EnterWriteLock();
try
{
// loop through and add each item
foreach (T item in ItemsToQueue)
m_Queue.Enqueue(item);
}
finally
{
LockQ.ExitWriteLock();
}
}
/// <summary>
/// Enqueues the list of items
/// </summary>
/// <param name="ItemsToQueue">list of items to enqueue</param>
public void EnqueueAll(TList<T> ItemsToQueue)
{
LockQ.EnterWriteLock();
try
{
// loop through and add each item
foreach (T item in ItemsToQueue)
m_Queue.Enqueue(item);
}
finally
{
LockQ.ExitWriteLock();
}
}
And, since we have an EnqueueAll, I also found a need to dequeue everything at once. DequeueAll returns a thread safe list (TList), instead of the standard List.
/// <summary>
/// Dequeues all the items and returns them as a thread safe list
/// </summary>
public TList<T> DequeueAll()
{
LockQ.EnterWriteLock();
try
{
// create return object
TList<T> returnList = new TList<T>();
// dequeue until everything is out
while (m_Queue.Count > 0)
returnList.Add(m_Queue.Dequeue());
// return the list
return returnList;
}
finally
{
LockQ.ExitWriteLock();
}
}