Reputation: 21095
I'm trying to serialize some LINQ to SQL generated objects to use with Memcached if at all possible. I am working on a new transcoder that will act as a new serializer to use with LINQ to SQL objects.
It seems like the DataContractSerializer
classes do not event attempt to serialize relational entities. For example, if I had this relationship:
Course -> CourseProject -> Project
The properties for Course
would be serialized, but the relational lists for CourseProject
and Project
would be empty, rather than null. I'm pretty sure this is a function of "Unidirectional" serializing to avoid cyclic redundancies.
Here is the problem with this... when loading items from cache the relationships are totally empty (empty IEnumerable
, not null
), and will never be loaded from the database if requested. Effectively, all projects for a course are lost when retrieving from cache.
If I could serialize even one or two levels deeper than just the base object it would be helpful. If that's not possible, then I would even prefer that unserialized data was lazy loaded from the database instead, though that sort of defeats the purpose of serializing and caching.
Upvotes: 0
Views: 1438
Reputation: 21095
Alright, after several days of banging my head on my desk I think I have this figured out to a workable level... there was a few things I really had to get a grasp on first:
So the real trick is to just make sure that you attach the object(s) as soon as you deserialize them. Here is a real world example from our system (we have some memcached wrappers in place for our system).
string cacheKey = "apSpace_GetActiveSpacesForPerson_PersonID:" + personid;
List<apSpace> list = MemCached.Get<List<apSpace>>(cacheKey);
if (list == null)
{
//Some complex, intensive query...
list = (from s in BaseDB.apSpaces
from so in BaseDB.apSpaceOwners
from sp in BaseDB.apSpacePersons
where (so.PersonID == personid
&& so.SpaceID == s.SpaceID
&& s.Deleted == false
&& s.IsArchived == false)
|| (sp.PersonID == personid
&& sp.SpaceID == s.SpaceID
&& s.Deleted == false
&& s.IsArchived == false)
select s).Distinct().OrderBy(s => s.Name).ToList();
//Cache the query result
MemCached.Store(cacheKey, list);
}
else
BaseDB.apSpaces.AttachAll(list); //Attach immediately!
return list;
Obviously this is not a silver bullet as far as caching related data, but it does allow you to preserve relationships and at the same time cache the initial result of intensive query. LINQ to SQL would be lazy-loading everything after that regardless.
Upvotes: 1
Reputation: 42246
I handled this problem by creating a generic wrapper which I used to lazy-load associations:
public class SqlRecord<T>{
where T: ISqlDataObject
public TAssociationRecord GetOneToOneAssociation<TAssociationRecord>(Expression<Func<T,TAssociationRecord>>> getAssociationPropertyExpression)
where TAssociationRecord : ISqlDataObject
{
}
public IEnumerable<TAssociationRecord> GetOneToManyAssociation(Expression<Func<T,EntitySet<TAssociationRecord>>> getAssociationPropertyExpression)
where TAssociationRecord: ISqlDataObject
{
}
}
Then I added a marker interface to all of the data objects that were automatically generated by LINQ to SQL:
public interface ISqlDataObject {}
I bet you could do something similar to meet your needs.
Upvotes: 1