So many things in LINQ to SQL are cool. But the handling of entities in a asp.net application drove me crazy. It was either the entity is already attached, different then the one in the database, or created with a different data context. So, I created my own base class that handles this and some other common issues with LINQ.
Let’s discuss.
The entities created by LINQ to SQL do not have a base class. As a result, it is VERY easy for them to inherit from anything, including my custom base class. Because the entity classes are already partial classes, all you need to do is create another class with the same name and the partial keyword. In addition, reference System.Runtime.Serialization. Done.
This is truly my gift to you. It took me a good 3 days to finally get all this working right. And the dynamic lambda stuff there (see the SelectOne() method) – that alone might make all this worth it. Nonetheless, it’s all yours. You’re welcome. :)
Here’s the code snippet to get you there (entity name = “Term”):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Runtime.Serialization;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq.Expressions;
using System.Reflection;
namespace MvcApplication1
{
public partial class Term : CustomEntityBase<Term, GlossaryDataContext, int>
{
static public IQueryable<Term> Search(string value)
{
return (Program.NewDataContext as GlossaryDataContext).Terms.Where(x => x.Word.Contains(value));
}
}
#region Implementation
// TODO: move to own file
public class Program
{
static private object m_DataContext;
static public object DataContext
{
get
{
if (m_DataContext == null)
m_DataContext = NewDataContext;
return m_DataContext;
}
}
static public object NewDataContext
{
get
{
return new GlossaryDataContext(); // custom connection string
}
}
}
// TODO: move to own file
public interface ICustomEntitySimple
{
void Update();
void Delete();
void Insert();
}
// TODO: move to own file
public interface ICustomEntity<E, C, T> : ICustomEntitySimple
{
string __KeyName
{
get;
}
T __KeyValue
{
get;
}
E Select(T key);
}
// begin base class
// TODO: reference System.Runtime.Serialization
[DataContract]
public abstract class CustomEntityBase<E, C, T> : ICustomEntity<E, C, T>
where E : class
where C : System.Data.Linq.DataContext
{
public CustomEntityBase()
{
}
public string __KeyName
{
get
{
if ((Program.DataContext as C).Mapping.GetTable(typeof(E)).RowType.IdentityMembers.Count == 0)
throw new Exception("No Primary Key set for entity");
if ((Program.DataContext as C).Mapping.GetTable(typeof(E)).RowType.IdentityMembers.Count > 1)
throw new Exception("Compound Primary Key detected; not supported");
string _KeyName;
_KeyName = (Program.DataContext as C).Mapping.GetTable(typeof(E)).RowType.IdentityMembers[0].Name;
if (string.IsNullOrEmpty(_KeyName))
throw new Exception("Primary Key property not found");
return _KeyName;
}
}
public T __KeyValue
{
get
{
PropertyInfo _KeyProperty;
_KeyProperty = typeof(E).GetProperty(__KeyName);
if (_KeyProperty == null)
throw new Exception("Primary Key property not found");
return (T)_KeyProperty.GetValue(this, null);
}
}
public static Table<E> SelectAll()
{
C _Context;
_Context = Program.NewDataContext as C;
Table<E> _Table;
_Table = _Context.GetTable(typeof(E)) as Table<E>;
return _Table;
}
public static E SelectOne(T key)
{
string _KeyName;
_KeyName = (Program.DataContext as C).Mapping.GetTable(typeof(E)).RowType.IdentityMembers[0].Name;
if (string.IsNullOrEmpty(_KeyName))
throw new Exception("Primary Key property not found");
// build query
ParameterExpression _ParameterExpression;
_ParameterExpression = Expression.Parameter(typeof(E), "x");
MemberExpression _LeftExpression;
_LeftExpression = MemberExpression.Property(_ParameterExpression, _KeyName);
Expression _RightExpression;
_RightExpression = Expression.Constant(key);
BinaryExpression _BinaryExpression;
_BinaryExpression = MemberExpression.Equal(_LeftExpression, _RightExpression);
Expression<Func<E, bool>> _LambdaExpression;
_LambdaExpression = Expression.Lambda<Func<E, bool>>
(
_BinaryExpression,
new ParameterExpression[] { _ParameterExpression }
);
C _Context;
_Context = Program.DataContext as C;
Table<E> _Table;
_Table = _Context.GetTable(typeof(E)) as Table<E>;
return _Table.SingleOrDefault(_LambdaExpression);
}
public virtual E Select(T key)
{
return SelectOne(key);
}
#region Helpers
private E DetatchedEntity
{
get
{
StringWriter _StringWriter;
_StringWriter = new StringWriter();
using (XmlWriter _XmlWriter = XmlWriter.Create(_StringWriter))
{
DataContractSerializer _DataContractSerializer;
_DataContractSerializer = new DataContractSerializer(this.GetType());
_DataContractSerializer.WriteObject(_XmlWriter, this);
}
string _SerializedValue;
_SerializedValue = _StringWriter.ToString();
using (StringReader _StringReader = new StringReader(_SerializedValue))
{
using (XmlReader _XmlReader = XmlReader.Create(_StringReader))
{
DataContractSerializer _DataContractSerializer;
_DataContractSerializer = new DataContractSerializer(this.GetType());
object _RawObject;
_RawObject = _DataContractSerializer.ReadObject(_XmlReader);
return (E)_RawObject;
}
}
}
}
private void Sync(E detachedEntity)
{
foreach (var _Property in this.GetType().GetProperties())
{
object _DetatchedValue;
_DetatchedValue = _Property.GetValue(detachedEntity, null);
if (_Property.CanWrite)
_Property.SetValue(this, _DetatchedValue, null);
}
}
private void ProcessInsert(ref E entity)
{
PropertyInfo _DateCreated;
_DateCreated = entity.GetType().GetProperty("DateCreated");
if (_DateCreated != null)
_DateCreated.SetValue(entity, DateTime.Now, null);
ProcessUpdate(ref entity);
}
private void ProcessUpdate(ref E entity)
{
PropertyInfo _DateModified;
_DateModified = entity.GetType().GetProperty("DateModified");
if (_DateModified != null)
_DateModified.SetValue(entity, DateTime.Now, null);
}
#endregion
#region ICustomEntity
public void Update()
{
C _Context;
_Context = Program.NewDataContext as C;
Table<E> _Table;
_Table = _Context.GetTable(typeof(E)) as Table<E>;
E _Entity = DetatchedEntity;
ProcessUpdate(ref _Entity);
_Table.Attach(_Entity);
_Context.Refresh(RefreshMode.KeepCurrentValues, _Entity);
_Context.SubmitChanges();
Sync(_Entity);
}
public void Delete()
{
C _Context;
_Context = Program.NewDataContext as C;
Table<E> _Table;
_Table = _Context.GetTable(typeof(E)) as Table<E>;
E _Entity = DetatchedEntity;
_Table.Attach(_Entity);
_Table.DeleteOnSubmit(_Entity);
_Context.SubmitChanges();
Sync(_Entity);
}
public void Insert()
{
C _Context;
_Context = Program.NewDataContext as C;
Table<E> _Table;
_Table = _Context.GetTable(typeof(E)) as Table<E>;
E _Entity = DetatchedEntity;
ProcessInsert(ref _Entity);
_Table.InsertOnSubmit(_Entity);
_Context.SubmitChanges();
Sync(_Entity);
}
#endregion
}
#endregion
}
0 comments:
Post a Comment