Als je gaat beginnen met Linq to SQL binneneen drie lagen architectuur wil je Linq beperken tot de data access laag zoals voorgeschreven in een 3-Tier applicatie. Ik heb daarom in de DL Layer een DA class gemaakt waarin ik static functies plaats met daarin de linq queries en insert, update en delete logica.
Zie onderstaande code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DLLExampleProject
{
public class DAOrder
{
/// <summary>
/// Get the Order by its OrderId
/// </summary>
/// <param name=”Id”>Order id</param>
/// <returns>Order object</returns>
public static Order GetOrderById(int Id)
{
using (var context = new NorthwindDataContext())
{
return (from o in context.Orders
where (o.OrderID == Id)
select o).Single();
}
}
/// <summary>
/// Update the order in the database
/// </summary>
/// <param name=”order”>Order to update</param>
public static void UpdateOrder(Order order)
{
using (var context = new NorthwindDataContext())
{
context.Orders.Attach(order);
context.SubmitChanges();
}
}
/// <summary>
/// Insert the order in the database
/// </summary>
/// <param name=”order”>Order to insert</param>
public static void InsertOrder(Order order)
{
using (var context = new NorthwindDataContext())
{
context.Orders.InsertOnSubmit(order);
context.SubmitChanges();
}
}
/// <summary>
/// Delete the order from the database
/// </summary>
/// <param name=”order”>Order to delete</param>
public static void DeleteOrder(Order order)
{
using (var context = new NorthwindDataContext())
{
context.Orders.DeleteOnSubmit(order);
context.SubmitChanges();
}
}
}
}
Binnen deze functies wordt elke keer een nieuwe Linq DataContext aangemaakt naar de database. Bij het updaten loop je dan echter stuk op de volgende error.
“System.NotSupportedException: An attempt has been made to Attach or Add an entity that is not new, perhaps
having been loaded from another DataContext. This is not supported..” Dit is het gevolg van het opnieuw creëren
van de DataContex.
Mijn eerste overweging was om de context binnen static te bewaren zodat ik de updates en deletes binnen de zelfde
context kan doen als de select. Dit heeft echter een groot nadeel. Ik zal het even uitleggen.
Indien je vanuit de business logic meerdere objecten hebt opgehaald en gewijzigd en je wil er één opslaan dan zullen door de SubmitChanges() alle wijzigingen worden gepersisteerd. Dit is niet wat je verwacht als je de functie UpdateOrder(order) uitvoert. In een web omgeving is het echter nog erger want je persisteert namelijk ook de wijzigingen van de andere gebruikers. Tevens wordt de database connectie continu open gehouden
door de context wat ook niet aan te bevelen is.
Ik ben daarna opzoek gegaan naar een methode om de objecten opnieuw aan nieuwe context te binden en heb daarvoor een kleine functie geschreven waarmee je de wijzigingen van een object kan kopiëren naar
een aan de nieuwe context gebonden object. Ik heb deze in een Base class van de DA classes geplaatst.
public class DABase
{
/// <summary>
/// Copy properties from two objects of the same type
/// </summary>
/// <typeparam name=”T”></typeparam>
/// <param name=”from”>object to get the values from</param>
/// <param name=”to”>object to set the values to</param>
public static void CopyObjectValues<T>(T from, T to)
{
foreach (PropertyInfo property in from.GetType().GetProperties())
{
if (property.GetCustomAttributes( typeof(ColumnAttribute), false).Count() > 0)
{
property.SetValue(to, property.GetValue(from, null), null);
}
}
}
}
Deze static functie kopieert alle public Properties waarbij een Column Attribute is gedefinieerd van het ene naar het
andere Object. Het Parent object en de Child objects (indien aanwezig) worden niet gekopieerd.
Na deze wijziging ziet de DAOrder classe er als volgt uit:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DLLExampleProject
{
public class DAOrder: DABase
{
/// <summary>
/// Get the Order by its OrderId
/// </summary>
/// <param name=”Id”>Order id</param>
/// <returns>Order object</returns>
public static Order GetOrderById(int Id)
{
using (var context = new NorthwindDataContext())
{
return (from o in context.Orders
where (o.OrderID == Id)
select o).Single();
}
}
/// <summary>
/// Update the order in the database
/// </summary>
/// <param name=”order”>Order to update</param>
public static void UpdateOrder(Order order)
{
using (var context = new NorthwindDataContext())
{
Order original = context.Orders.Single(o => o.OrderID == order.OrderID);
CopyObjectValues(order, original);
context.SubmitChanges();
}
}
/// <summary>
/// Insert the order in the database
/// </summary>
/// <param name=”order”>Order to insert</param>
public static void InsertOrder(Order order)
{
using (var context = new NorthwindDataContext())
{
context.Orders.InsertOnSubmit(order);
context.SubmitChanges();
}
}
/// <summary>
/// Delete the order from the database
/// </summary>
/// <param name=”order”>Order to delete</param>
public static void DeleteOrder(Order order)
{
using (var context = new NorthwindDataContext())
{
Order original = context.Orders.Single(o => o.OrderID == order.OrderID);
context.Orders.DeleteOnSubmit(original);
context.SubmitChanges();
}
}
}
}

