After after reading Rob Conery's post http://blog.wekeroad.com/blog/crazy-talk-reducing-orm-friction/. I thought I'd give it a whirl and didn't get very far before I ran into my first roadblock. DB4O version 7.4.68 doesn't support any .NET System.Transaction. (For what I could tell...)
Below I'll explain how I pieced this together and made it work (At least an alpha type version to work...)
Assume I've followed almost everything Rob has in his blog for setup (IRepository, DB4O container class, ObjectRepository<T>, etc...)
My first unit test was the following
[Test]
public void CanRollbackSavedObjectInRepository()
{
using (TransactionScope scope = new TransactionScope())
{
_repository.Save(_dummyObject);
//NO: scope.Complete(); Implied rollback
}
if (_repository.GetAll().Count() > 0)
Assert.Fail("TransactionScope Failed - count[" + _repository.GetAll().Count().ToString() + "]");
}
And the above test fails...
Next was some research to figure out why this was failing... I didn't find much out there, except for some pending TODO's on the DB4O project.
http://tracker.db4o.com/browse/COR-1376
http://tracker.db4o.com/browse/COR-1143
So then it was off to figure out how frameworks implemented there resource managers to hook into the System.Transaction goo...
(Long story short, and several trial and error failures) The best of the solutions I found was in this open source code base. (http://code.google.com/p/uniframework/)
In that project there was a class that managed the enlistment of a transaction, and some examples of how to use it...
http://code.google.com/p/uniframework/source/browse/trunk/sources/Uniframework/Db4o/Db4oEnlist.cs is the enlistment class.
I placed this Db4oEnlist class as a private nested class inside the ObjectRepository<T> since I can't see why it would be used anywhere else...
And below is how I use them in the ObjectRepository<T>.
public void Delete(T item)
{
Db4oEnlist enlist = new Db4oEnlist(Container, item);
bool inTransaction = Enlist(enlist);
Container.Delete(item);
if (!inTransaction)
Container.Commit();
}
public void Save(T item)
{
Db4oEnlist enlist = new Db4oEnlist(Container, item);
bool inTransaction = Enlist(enlist);
Container.Store(item);
if (!inTransaction)
Container.Commit();
}
We first create an instance of the Db4OEnlist class with the current container. This class implements the IEnlistmentNotification interface and knows how to commit/rollback/etc on the object database. We then use the private helper method Enlist() giving it the Db4OEnlist instance. This helper method enlists the sequence in any existing transactions returning if it enlisted in a transaction or not.
private static bool Enlist(Db4oEnlist enlist)
{
System.Transactions.Transaction currentTx = System.Transactions.Transaction.Current;
if (currentTx != null)
{
currentTx.EnlistVolatile(enlist, EnlistmentOptions.None);
return true;
}
return false;
}
If we aren't in a transaction we commit the action right away, however if we are in the transaction we let the .net System.Transaction framework take care of committing the transaction.
Once I found the example in the "uniframework" it came together rather quickly...
Below is the ObjectRepository<T> I'm going forward with on testing Rob's idea of developing an application using the object database first... (we'll see how it goes)
public class ObjectRepository<T> :
IRepository<T> where T : class
{
/// <summary>
/// Returns all T records in the repository
/// </summary>
public IQueryable<T> GetAll()
{
return (from T items in Container
select items).AsQueryable();
}
/// <summary>
/// Finds an item using a passed-in expression lambda
/// </summary>
public IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> expression)
{
return GetAll().Where(expression);
}
/// <summary>
/// Saves an item to the database
/// </summary>
/// <param name="item"></param>
public void Save(T item)
{
Db4oEnlist enlist = new Db4oEnlist(Container, item);
bool inTransaction = Enlist(enlist);
Container.Store(item);
if (!inTransaction)
Container.Commit();
}
/// <summary>
/// Deletes an item from the database
/// </summary>
/// <param name="item"></param>
public void Delete(T item)
{
Db4oEnlist enlist = new Db4oEnlist(Container, item);
bool inTransaction = Enlist(enlist);
Container.Delete(item);
if (!inTransaction)
Container.Commit();
}
private static bool Enlist(Db4oEnlist enlist)
{
System.Transactions.Transaction currentTx = System.Transactions.Transaction.Current;
if (currentTx != null)
{
currentTx.EnlistVolatile(enlist, EnlistmentOptions.None);
return true;
}
return false;
}
private static IObjectContainer Container
{
get
{
return ServiceLocator.Current.GetInstance<IObjectContainer>();
}
}
/// <summary>
/// Provides support for System.Transaction integration
/// </summary>
private class Db4oEnlist : IEnlistmentNotification
{
private IObjectContainer container;
private object oldItem;
/// <summary>
/// Initializes a new instance of the <see cref="db4oEnlist"/> class.
/// </summary>
/// <param name="database">The database.</param>
/// <param name="item">The item.</param>
public Db4oEnlist(IObjectContainer container, object item)
{
this.container = container;
oldItem = item;
}
#region IEnlistmentNotification
/// <summary>
/// Commits the specified enlistment.
/// </summary>
/// <param name="enlistment">The enlistment.</param>
public void Commit(Enlistment enlistment)
{
container.Commit();
oldItem = null;
}
/// <summary>
/// Ins the doubt.
/// </summary>
/// <param name="enlistment">The enlistment.</param>
public void InDoubt(Enlistment enlistment)
{
//throw new Exception("The method or operation is not implemented.");
}
/// <summary>
/// Prepares the specified preparing enlistment.
/// </summary>
/// <param name="preparingEnlistment">The preparing enlistment.</param>
public void Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
}
/// <summary>
/// Rollbacks the specified enlistment.
/// </summary>
/// <param name="enlistment">The enlistment.</param>
public void Rollback(Enlistment enlistment)
{
container.Rollback();
container.Ext().Refresh(oldItem, int.MaxValue);
}
#endregion
}
}
___________________
Dissertation Sample
___________________
_____________________________
Dissertation Topics
Thank you for your response.
My Xml looks like
University1
Course1
Student1
Student2
University1
Course2
Student3
Student4
University2
Course1
Student5
etc...
XElement cimXml = XElement.Load(@"C:\Students.xml");
So, here I am grouping on Universities.
var universityGroupedElements = from ele in cimXml.Elements() group ele by ele.Name;
Now I want to add indexes to University, Course, Student elements to make queries faster.
Also, I want to group the courses in each university and also students in each course
Please help, in writing some code.
I would be happy to help you, but have a request and a couple questions...
1. I've realized my blog comments are starting to become a bit more of the i4o knowledge base than it deserves. Would you please ask your question over on the i4o Discussion board?
2. I'd like a little more detail. What does the xml structure look like? What does the linq query look like that returns your "grouped" data? What is it you are trying to index/search?
Thanks,
Jason
University
Course
Student
I have list of Students from a various Courses and various Universities. I used Linq for Grouping Universities and Courses of Students. Now, I have question how to implement Indexing on Students, Courses and Universities.
Please give the implementation details .
Thanks
Satya
For example:
Instead of writing this..
var f = indexedFileInfosFromDir.Where(fi => fi.Extension == "txt" && fi.IsReadOnly == true);
Try this..
var f = indexedFileInfosFromDir.Where(fi => fi.Extension == "txt");
var f2 = f.Where(fi.IsReadOnly == true);
(fi.Extension should be smaller group than fi.IsReadOnly)
From what I have tested,
It responses within a few milisecond.
I am happy with this.
Thank you for great work.
Please keep in mind that this library is very simple, and is not a complete Linq implementation.
It provides some great benefits in the scenarios it was designed for. And I know Aaron has some more improvements on the way.
If you create any patches for the project, we are happy to take a look at any improvements you can come up with.
Thanks again
From the 'Demoi4o' project.
If I change this below query
var studentsNamedAaronFromConstant =
from student in _testStudents
where student.FirstName == studentNameBox.Text
select student;
To
var studentsNamedAaronFromConstant =
from student in _testStudents
where student.FirstName.Contains(studentNameBox.Text)
select student;
It takes longest time!!
If this is what you were asking, then no it will not update the index.
You need to call the IndexableCollection<T>.Add/Remove and not it's base type Collection<T>.Add/Remove for the indexes to be updated.
This is however something we'd like to support in the future, which is why I checked in the failing test. We probably need to just implement ICollection<T> etc... and to fully support this.
I have a question.
If we have some changes on our base collection like add or delete, do we have to re create index or do some special steps?
Thanks,
Paiwan
I was excited when I saw your post and really like the syntax for adding properties to the IndexSpecification. Now, I guess I'll just wait until I can actually use it like that. From Aaron's blog, it sounds like he plans to introduce updates to the Where expression in the next release.
Thanks for your work on this library. I'm hoping this will make it possible to replace a subsytem of our app which currently relies on looking up factors in large in-memory sets of data by using XML. It loads large XML documents into memory and builds XPath queries to get to individual factors that it needs. The XPath queries themselves are pretty past, but I'm trying to use Linq-to-XML to project the XML into collections of strongly type objects that I can query with Where lambda's instead. It works well, but when the collections are large enough the queries are too much slower than the XPath version. I'm hoping the IndexableCollection will make it possible.
Kevin
I was just illustrating the power of the IndexSpecification. Unfortunately the extension methods used to evaluate the expression need some work.
Thanks for this post. I got to it from the i4o home page on Codeplex. I noticed in the discussions that someone mentioned the fact that if you have multiple expressions in your Where expression, that it won't use the index.
So does that mean, your example showing multiple properties being added to the index doesn't really help if you wanted to search your collection on both of the properties in the index?
For example, would this code use the index?
var f = indexedFileInfosFromDir.Where(fi => fi.Extension == "txt" && fi.IsReadOnly == true);
From my testing, it appears that no benefit is achieved from using an IndexableCollection vs. a regular List in this case. Seems like there's no point in using the IndexableCollection if you need more than one property in the index. Am I missing something or is that correct?
Thanks,
Kevin