Views/RowSchemas

Nov 17, 2012 at 7:37 AM

Hi,

Lastest issue i am having is with the RowScemas needing to extend a class from RaptorDB.  I am working on building my code to be database independent and that works perfectly fine for the entities for this database but i hit an issue with the view/rowschema.  This also becomes an issue in the repoistory pattern as since you query the rowschema and it extends a class from the raptorDB then any thing using the repository also need the RaptorDB common dll included in their project tieing everything to the database.

I am wondering if there is anyway the views could be changed so that you basically end up with another entity for the view that has no references to anything else like a normal entity and this is what you use in searches and search results.  You then either have a third class that likes the view entity and normal entity together and configures the view or you just have a series of methods or something you can call to do it. All my issues seem to relate to the face that i need to use attributes on the view entity and also extend RowSchema.  I tired using an interface on the view which worked quite well apart from searching and counts because they were then done on the interface and the system could not match the interface to the viewname.

 

Just an idea that only just popped into my head but would it be possible to make it so that you could declare extra types on a RowSchema that would match that view?  As in i make my RowSchema also implement interface ITestView and in the constructor or something i call a base method to also register "ITestView" as a type that can be used to query that view. That way you have have just the data interface declared in a common project and it would nto need any reference to RaptorDB removing the dependency.  Then the RowSchema can just implement that interface.  Queries can be done against the interface then so nothing needs to know the real database i am using. Does that make sense?  Basically Add view would call a method on the RowSchema to get potential other types that will match the view that the RowSchema implements.

 

 

Coordinator
Nov 17, 2012 at 8:09 AM

If you implement {get;set;} for your properties instead of fields and add a Guid DocID property to your schema then you are isolated from RaptorDB ( the dependency was for field property bindings working correctly and the docid property).

Nov 17, 2012 at 8:11 AM

I have a working fix and tests so far are looking good.  there may be a better way to do this i am nto sure and this requires the latest source from codeplex to work i suspect but here goes.

 

Firstly a change to View.cs to add the following method to class View<T>

public virtual Type[] GetExtraTypesToMap()
        {
            return new Type[0];
        }

Then a change to Viewmanager.cs on line 264 to add the new type.  I will include some lines around the change so it is easy to work out where it goes.

 // add view schema mapping 
                _otherViewTypes.Add(view.Schema, view.Name.ToLower());
//start of change
                foreach (Type type in view.GetExtraTypesToMap())
                {
                    if (type.IsAssignableFrom(view.Schema))
                    {
                        _otherViewTypes.Add(type, view.Name.ToLower());
                    }
                    else
                    {
                        throw new Exception(string.Format("Type {0} is not inherited by {1} and therefore cannot be added to GetExtraTypesToMap()", type, view.Schema));
                    }
                }
//end of change

                //_viewAQFNmapping.Add(view.GetType().AssemblyQualifiedName, view.Name.ToLower());
                //_viewAQFNmapping.Add(view.Schema.AssemblyQualifiedName, view.Name.ToLower());

As a sample of how to use it i have included the code below

//The query
 Result<ITestView> result2 = data.Query<ITestView>(a => (a.Id == "1"));

//The Interface
public interface ITestView
    {
        Guid UnquieID { get; set; }
        //public SourceIds SourceId;
        int SourceId { get; set; }
        //public DateTime? LastCheckedForChanges;
        DateTime LastCheckedForChanges { get; set; }
        string Id { get; set; }
    }

//The View
     [RegisterView]
    public class NameListIdView : View<NameList>
    {
         public class RowSchema : RowSchemaBase, ITestView  // define the schema for this view
        {
             public Guid UnquieID { get; set; }
            //public SourceIds SourceId;
             public int SourceId { get; set; }
            //public DateTime? LastCheckedForChanges;
             public DateTime LastCheckedForChanges { get; set; }
             public string Id { get; set; }
           
            public override Type GetViewType()
            {
                return typeof(NameListIdView);
            }
        }

         public override Type[] GetExtraTypesToMap()
         {
             return new Type[] { typeof(ITestView) };
         }
         public NameListIdView()
        {
           
            this.Name = "NameListIdView";
            this.Description = "Ids linked to each NameList";
            this.isPrimaryList = false;
            this.isActive = true;
            this.BackgroundIndexing = false;
            this.Version = 1;
            this.Schema = typeof(NameListIdView.RowSchema);

            this.AddFireOnTypes(typeof(NameList));

            this.Mapper = (api, docid, doc) =>
            {
                foreach (var dsd in doc.DataSourceDetails)
                {
                    api.Emit(docid, doc.EntityID, (int)dsd.DataSourceIDType, dsd.LastCheckedForChanges.HasValue?dsd.LastCheckedForChanges:DateTime.MinValue, dsd.DataSourceID);
                }
            };
        }
    }

Nov 17, 2012 at 8:15 AM
MGholam wrote:

If you implement {get;set;} for your properties instead of fields and add a Guid DocID property to your schema then you are isolated from RaptorDB ( the dependency was for field property bindings working correctly and the docid property).

Tried that but it throws an exception saying "The schema must be derived from RaptorDb.RDBSchema"  which is the whole problem.  Been able to use your own interface is actually working very nicely

Coordinator
Nov 17, 2012 at 1:18 PM

I will make it so that the RDBSchema is optional and if it the row schema is not inherited it will check if you have a docid property/field defined. (you will have to use properties instead of fields so binding will work).

This way you can define the row schema elsewhere and use it in your view.

Nov 17, 2012 at 7:56 PM

That would fix everything apart from one thing which is the attribute [FullText].  I am liking the interface idea i had more and more because then i can also name the interface more as a search object and make the properties only have a get method.  The interface makes it all nice and generic and since it just has the property decelerations it would not have the attributes etc.  Then the implementation can have get/set and attributes like it does now meaning there is no change for anyone not using repository pattern, dependency injection or onion design.  That fixes all the issues i had with the view design that did not feel quite right.  I will probably change my modification to use a property on the view or a method on the view to add the extra types. I thought about grabbing any interfaces the rowschema implementation implements automatically but then realised you could easily get classes and other issues then so it is better to manually set them.

 

You really have done a great job with this database!  while i have hit a few issues here and there the design is allowing most of them to be fixed quickly and easily in most cases and every system has a few issues. I know i will be using your database for all my apps where i don't want to install a relation database which is 90% of the stuff i build/work on when not at work.  I have tried many object DB's but yours seems to be the fastest and the most flexible.  Great job.

Nov 17, 2012 at 8:13 PM
Edited Nov 17, 2012 at 8:17 PM

This is my latest implementation.  I changed it a little to make it nicer to use and fit more with your design of the rest of the system.

 

public class View<T> : ViewBase
    {
        public List<Type> AdditionalSchemaTypes { get; private set; }
        public delegate void MapFunctionDelgate<V>(IMapAPI api, Guid docid, V doc);
        public View()
        {
            isActive = true;
            FireOnTypes = new List<string>();
            DeleteBeforeInsert = true;
            BackgroundIndexing = true;
            //AllowTransactions = false;
            AdditionalSchemaTypes = new List<Type>();
        }

        /// <summary>
        /// Inline delegate for the mapper function used for quick applications 
        /// </summary>
        [XmlIgnore]
        public MapFunctionDelgate<T> Mapper { get; set; }

        public Result<object> Verify()
        {
            if (Name == null || Name == "") 
                throw new Exception("Name must be given");
            if (Schema == null) 
                throw new Exception("Schema must be defined");
            if (Schema.IsSubclassOf(typeof(RDBSchema)) == false) 
                throw new Exception("The schema must be derived from RaptorDB.RDBSchema");
            if (Mapper == null) 
                throw new Exception("A map function must be defined");
            if (FireOnTypes.Count == 0) 
                throw new Exception("No types have been defined to fire on");
            if (TransactionMode == true && isPrimaryList == false)
                throw new Exception("Transaction mode can only be enabled on Primary Views");

            foreach (Type type in AdditionalSchemaTypes)
            {
                if (!type.IsAssignableFrom(Schema))
                {
                    throw new Exception(string.Format("Type {0} is not inherited by {1} and therefore cannot be added to AdditionalSchemaTypes", type, Schema));
                }
            }
            // FEATURE : add more verifications
            return new Result<object>(true);
        }
    }


//next part is a method from View Manager that i updated
internal void RegisterView<T>(View<T> view)
        {
            view.Verify();

            ViewHandler vh = null;
            if (_views.TryGetValue(view.Name.ToLower(), out vh))
            {
                _log.Error("View already added and exists : " + view.Name);
                //vh.RebuildExisting(_objectStore, view);
            }
            else
            {
                vh = new ViewHandler(_Path, this);
                vh.SetView(view , _objectStore);
                _views.Add(view.Name.ToLower(), vh);
                _otherViewTypes.Add(view.GetType(), view.Name.ToLower());

                // add view schema mapping 
                _otherViewTypes.Add(view.Schema, view.Name.ToLower());
		//------- start of my change
                foreach (Type type in view.AdditionalSchemaTypes)
                {
                    _otherViewTypes.Add(type, view.Name.ToLower());
                }
		//---- end of my change

                //_viewAQFNmapping.Add(view.GetType().AssemblyQualifiedName, view.Name.ToLower());
                //_viewAQFNmapping.Add(view.Schema.AssemblyQualifiedName, view.Name.ToLower());

                if (view.isPrimaryList)
                {
                    foreach (string tn in view.FireOnTypes)
                        _primaryView.Add(Type.GetType(tn), view.Name.ToLower());
                }
                else
                {
                    if (view.ConsistentSaveToThisView)
                        AddToViewList(_consistentViews, view);
                    else
                        AddToViewList(_otherViews, view);
                }
            }
        }


Then to use it is simple.  all you do is the following in your view constructor
this.Schema = typeof(NameListIdView.RowSchema);
this.AdditionalSchemaTypes.Add(typeof(INameListIdView));

The only other thing you need to do is put the interface on the rowschema implementation for the view otherwise validation will complain.

----- EDIT ---

Had to fix a few mistake sin the code above