Linq to Lucene for Entity Framework

Dec 27, 2009 at 2:25 PM

Basically Linq to Lucene uses L2S  but, if you use incorporate entity framework . what can u do

I wrote a class for Entity Framework : some changed 'DatabaseIndexSet' equivalent. add just one class to lucene.linq project, u can use EF instead of L2S

I hope that integrate to  'lucene.linq' project this class for entity framework user.

L2S uses System.Linq.Datacontext,

EF uses System.Data.Objects.ObjectContext

 

'DatabaseIndexSet' uses System.Linq.Datacontext

'DatabaseEFIndexSet' uses System.Data.Objects.ObjectContext and EDM instead of 'ITable'

 

below  'DatabaseEFIndexSet'  one class support entity framework

using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using System.Data.SqlClient;
using System.Reflection;
using Lucene.Linq.Mapping;
using Lucene.Linq.Utility;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Analysis;

using Lucene.Net.Search;
using Lucene.Net.QueryParsers;
using Lucene.Linq.Expressions;
using Lucene.Net.Store;
using System.Threading;
using System.Data.Linq;
using System.Linq.Expressions;

using System.Collections;
using System.Data.Objects;
using System.Data.Objects.DataClasses;

/************************************
 * Class Name : DatabaseEFIndexSet
 * Author : sharkspin@gmail.com
 * Description : Class for USE EF in Lucene.Linq, equivalent 'DatabaseIndexSet' 
 * blog: www.smack.kr, korean
 * notice : not fully tested , not guarantee 
 ***********************************/


namespace Lucene.Linq {

    /// <summary>Index Set for a LINQ to SQL ObjectContext
    /// TODO: DC instance rebuilding see http://www.codeplex.com/linqtolucene/WorkItem/View.aspx?WorkItemId=6072
    /// </summary>
    /// <typeparam name="TObjectContext">The ObjectContext type</typeparam>
    public class DatabaseEFIndexSet<TObjectContext>: IndexSet
        where TObjectContext : ObjectContext {
        #region Fields/Properties

        readonly ReaderWriterLockSlim _dataContextLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
        TObjectContext _dataContext;
        

        /// <summary>
        /// The data context instance. 
        /// Data Contexts should be recycled periodically based on unit-of-work basis.
        /// 
        /// </summary>
        public TObjectContext ObjectContext
        {
            get
            {
                return _dataContext;
            }
            set {
                if (value == null) 
                    throw new ArgumentNullException("value");

                using (_dataContextLock.WriteLock()) {
                    _dataContext = value;
                }
            }
        }

        #endregion

        #region Ctors/Init

        /// <summary>Creates RAM Indexes from data context</summary>
        /// <param name="dataContext">The data context instance</param>
        public DatabaseEFIndexSet(TObjectContext dataContext)
            : base() {
            if (dataContext == null)
                throw new ArgumentNullException("dataContext");

            ObjectContext = dataContext;
            Init();
        }

        ///<summary>Creates File System Indexes from data context</summary>
        ///<param name="path">File System path</param>
        /// <param name="dataContext">The data context instance</param>
        public DatabaseEFIndexSet(string path, TObjectContext dataContext):base(path) {
            if (dataContext == null)
                throw new ArgumentNullException("dataContext");

            ObjectContext = dataContext;
            Init();
        }

        ///<summary>Creates file system indexes from data context</summary>
        ///<param name="directory">File system directory info</param>
        /// <param name="dataContext">The data context instance.</param>
        public DatabaseEFIndexSet(DirectoryInfo directory, TObjectContext dataContext)
            : base(directory) {

            if (dataContext == null)
                throw new ArgumentNullException("dataContext");

            ObjectContext = dataContext;
            Init();
        }



        private void Init() {

            Type dcType = typeof(TObjectContext);

            // get all tables in db context
            var linqTableTypes = GetTableTypes();

            int linqTableCount = linqTableTypes.Count();

            // Throw if there are no tables
            if (linqTableCount == 0)
            {
                throw new ArgumentException("The data context type has no Tables");
            }

            // if there are any tables on the context, add them to the set
            foreach (var linqTableType in linqTableTypes)
            {
                try
                {
                    Add(linqTableType);
                }
                catch (ArgumentException)
                {
                    // linq table type doesn't have Document attribute
                    // for now, skipping this table type makes sense
                }
            }

                
            

        }

        #endregion
        
        #region Private Methods

        private IEnumerable<Type> GetTableTypes() {

            Type iTableType = typeof(EntityObject);
            var linqTableTypes = from prop in typeof(TObjectContext).GetProperties()
                                 where prop.PropertyType.IsClass && prop.PropertyType.Name =="ObjectQuery`1"
                                 select prop.PropertyType.GetGenericArguments()[0];
            //var linqTableTypes = from prop in typeof(TObjectContext).GetProperties()
            //                     where prop.PropertyType.IsGenericType &&
            //                           iTableType.IsAssignableFrom(prop.PropertyType)
            //                     select prop.PropertyType.GetGenericArguments()[0];

            return linqTableTypes;
        }

        private int GetTableRecordCount(ITable table, Type tableType) {
            Expression expr = Expression.Call(typeof(Queryable), "Count", new Type[] { tableType }, Expression.Constant(table));
            int count = table.Provider.Execute<int>(expr);
            return count;
            
        }


        #endregion

        #region Public Methods

        /// <summary>Write all the records from the table type into their respective indexes</summary>
        /// <typeparam name="TEntity">Table type to index</typeparam>
        public void Write<TEntity>() {
            //Write(typeof(TEntity));


            using (_dataContextLock.ReadLock())
            {
                Type tableType = typeof(TEntity);
                string name = tableType.Name;

                IIndex index = this.Get(tableType);

                ObjectQuery<TEntity> objectQuery = _dataContext.CreateQuery<TEntity>(name);
                if (objectQuery == null)
                    throw new ArgumentException("tableType doesnt belong to db");

                int itemCount = objectQuery.Execute(MergeOption.NoTracking).Count();

                var items = objectQuery.AsEnumerable();

                Console.WriteLine("About to write " + name + "s...");


                if (itemCount == index.Count)
                {
                    Console.WriteLine("Not adding " + name + "s, because index is up to date.");
                }
                else
                {
                    index.Add(items);
                    Console.WriteLine("Added " + index.Count + " " + name + "s.");
                } 
            }




        }




  
        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="tableType"></param>
        //public void Write(Type tableType)
        //{
        //    using (_dataContextLock.ReadLock())
        //    {
        //        string name = tableType.Name;

        //        IIndex index = this.Get(tableType);

        //        ObjectQuery objectQuery = _dataContext.CreateQuery<tableType>


        //        if (objectQuery == null)
        //            throw new ArgumentException("tableType doesnt belong to db");

        //        int itemCount = objectQuery.Execute(MergeOption.NoTracking).Count();

        //        var items = objectQuery.AsEnumerable();

        //        Console.WriteLine("About to write " + name + "s...");


        //        if (itemCount == index.Count)
        //        {
        //            Console.WriteLine("Not adding " + name + "s, because index is up to date.");
        //        }
        //        else
        //        {
        //            index.Add(items);
        //            Console.WriteLine("Added " + index.Count + " " + name + "s.");
        //        }
        //    }
        //}



        ///// <summary>Write all the records from each table into their respective indexes</summary>
        //public void Write() {
        //    using (_dataContextLock.ReadLock()) {
        //        var tableTypes = GetTableTypes();
        //        if (tableTypes.Count() > 0) {
        //            foreach (var tableType in tableTypes) {
        //                try {
        //                    Write(tableType);
        //                } catch (ArgumentException) {
        //                    // skip this table type
        //                }
        //            }
        //        }
        //    }


        //}

        #endregion

    }
}

usage :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data.Common;
using Lucene.Linq;
using System.Data.Linq;
using Lucene.Linq.Expressions;
using System.Diagnostics;
using Lucene.Linq.Utility;
using Lucene.Net.Search;
using Lucene.Net.Index;

namespace Lucene.Linq.EntityDemo
{
    class Program
    {
        static NorthwindIndexContext index = null;

        static void Main(string[] args)
        {

            var path = @"C:\temp\index";
            try
            {
                System.IO.Directory.Delete(path, true);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            System.IO.Directory.CreateDirectory(path);
            Console.WriteLine("Northwind Db index:" + path);

            //Code for L2S , Not necessary for EF
            //Type cType = typeof(Customers);

            index = new NorthwindIndexContext(path, new NorthwindContext());

            // add all the customers/orders to the index
            //Code For L2S
            //index.Write(); // uncomment this line to write both Orders and Customers
            
            //Code For EF, Generic 
            index.Write<Customers>();

            DefaultFieldsDemo();

            System.Console.WriteLine("Press any key to continue...");
            System.Console.ReadLine();

        }


        static void SimpleDemo()
        {
            var query = from c in index.Customer
                        where c.ContactTitle == "Owner"
                        select c;

            Console.WriteLine("Simple Query:");
            ObjectDumper.Write(query);
            Console.WriteLine("Query Output: {0}", query.ToString());
            Console.WriteLine();

            var results = query.ToList();
            Console.WriteLine("SimpleDemo returned {0} results", results.Count);
        }

        static void DefaultFieldsDemo()
        {
            var query = from c in index.Customer
                        where (c.Match("Folies"))
                        select new { Name = c.ContactName, Id = c.CustomerID, Company = c.CompanyName };

            Console.WriteLine("Default Fields Query:");
            ObjectDumper.Write(query);
            Console.WriteLine("Query Output: {0}", query.ToString());
            Console.WriteLine();
        }

    }




}

===================

visit my blog more detail  http://www.smack.kr/346 , but korean

Coordinator
Jan 11, 2010 at 9:21 AM

Hi,

 

I like it. thanks for writing this.

I can merge it into version control if you can do two things.

1. Add unit tests

2. Create a patch so i can apply the patch and it should work.

Thanks again.

 

Regards,

CV