Ejemplo n.º 1
0
    public static FluentInclude <T> WithVirtualMList <T, L>(this FluentInclude <T> fi,
                                                            Expression <Func <T, MList <L> > > mListField,
                                                            Expression <Func <L, Lite <T>?> > backReference,
                                                            Action <L, T>?onSave   = null,
                                                            Action <L, T>?onRemove = null,
                                                            bool?lazyRetrieve      = null,
                                                            bool?lazyDelete        = null) //To avoid StackOverflows
        where T : Entity
        where L : Entity
    {
        fi.SchemaBuilder.Include <L>();

        var mListPropertRoute  = PropertyRoute.Construct(mListField);
        var backReferenceRoute = PropertyRoute.Construct(backReference, avoidLastCasting: true);

        if (fi.SchemaBuilder.Settings.FieldAttribute <IgnoreAttribute>(mListPropertRoute) == null)
        {
            throw new InvalidOperationException($"The property {mListPropertRoute} should have an IgnoreAttribute to be used as Virtual MList");
        }

        RegisteredVirtualMLists.GetOrCreate(typeof(T)).Add(mListPropertRoute, new VirtualMListInfo(mListPropertRoute, backReferenceRoute));

        var defLazyRetrieve = lazyRetrieve ?? (typeof(L) == typeof(T));
        var defLazyDelete   = lazyDelete ?? (typeof(L) == typeof(T));

        Func <T, MList <L> >  getMList = GetAccessor(mListField);
        Action <L, Lite <T> >?setter   = null;
        bool preserveOrder             = fi.SchemaBuilder.Settings.FieldAttributes(mListPropertRoute) !
                                         .OfType <PreserveOrderAttribute>()
                                         .Any();

        if (preserveOrder && !typeof(ICanBeOrdered).IsAssignableFrom(typeof(L)))
        {
            throw new InvalidOperationException($"'{typeof(L).Name}' should implement '{nameof(ICanBeOrdered)}' because '{ReflectionTools.GetPropertyInfo(mListField).Name}' contains '[{nameof(PreserveOrderAttribute)}]'");
        }

        var sb = fi.SchemaBuilder;

        if (defLazyRetrieve)
        {
            sb.Schema.EntityEvents <T>().Retrieved += (T e, PostRetrievingContext ctx) =>
            {
                if (ShouldAvoidMListType(typeof(L)))
                {
                    return;
                }

                var mlist = getMList(e);

                if (mlist == null)
                {
                    return;
                }

                var query = Database.Query <L>()
                            .Where(line => backReference.Evaluate(line).Is(e.ToLite()));

                MList <L> newList = preserveOrder ?
                                    query.ToVirtualMListWithOrder() :
                                    query.ToVirtualMList();

                mlist.AssignAndPostRetrieving(newList, ctx);
            };
        }

        if (preserveOrder)
        {
            sb.Schema.EntityEvents <T>().RegisterBinding <MList <L> >(mListField,
                                                                      shouldSet: () => !defLazyRetrieve && !VirtualMList.ShouldAvoidMListType(typeof(L)),
                                                                      valueExpression: (e, rowId) => Database.Query <L>().Where(line => backReference.Evaluate(line).Is(e.ToLite())).ExpandLite(line => backReference.Evaluate(line), ExpandLite.ToStringLazy).ToVirtualMListWithOrder(),
                                                                      valueFunction: (e, rowId, retriever) => Schema.Current.CacheController <L>() !.Enabled ?
                                                                      Schema.Current.CacheController <L>() !.RequestByBackReference <T>(retriever, backReference, e.ToLite()).ToVirtualMListWithOrder():
                                                                      Database.Query <L>().Where(line => backReference.Evaluate(line).Is(e.ToLite())).ExpandLite(line => backReference.Evaluate(line), ExpandLite.ToStringLazy).ToVirtualMListWithOrder()

                                                                      );
        }
        else
        {
            sb.Schema.EntityEvents <T>().RegisterBinding(mListField,
                                                         shouldSet: () => !defLazyRetrieve && !VirtualMList.ShouldAvoidMListType(typeof(L)),
                                                         valueExpression: (e, rowId) => Database.Query <L>().Where(line => backReference.Evaluate(line).Is(e.ToLite())).ExpandLite(line => backReference.Evaluate(line), ExpandLite.ToStringLazy).ToVirtualMList(),
                                                         valueFunction: (e, rowId, retriever) => Schema.Current.CacheController <L>() !.Enabled ?
                                                         Schema.Current.CacheController <L>() !.RequestByBackReference <T>(retriever, backReference, e.ToLite()).ToVirtualMList() :
                                                         Database.Query <L>().Where(line => backReference.Evaluate(line).Is(e.ToLite())).ExpandLite(line => backReference.Evaluate(line), ExpandLite.ToStringLazy).ToVirtualMList()
                                                         );
        }

        sb.Schema.EntityEvents <T>().PreSaving += (T e, PreSavingContext ctx) =>
        {
            if (VirtualMList.ShouldAvoidMListType(typeof(L)))
            {
                return;
            }

            var mlist = getMList(e);
            if (mlist == null)
            {
                return;
            }

            if (mlist.Count > 0)
            {
                var graph = Saver.PreSaving(() => GraphExplorer.FromRoot(mlist).RemoveAllNodes(ctx.Graph));
                GraphExplorer.PropagateModifications(graph.Inverse());
                var errors = GraphExplorer.FullIntegrityCheck(graph);
                if (errors != null)
                {
#if DEBUG
                    var withEntites = errors.WithEntities(graph);
                    throw new IntegrityCheckException(withEntites);
#else
                    throw new IntegrityCheckException(errors);
#endif
                }
            }

            if (mlist.IsGraphModified)
            {
                e.SetSelfModified();
            }
        };

        sb.Schema.EntityEvents <T>().Saving += (T e) =>
        {
            if (VirtualMList.ShouldAvoidMListType(typeof(L)))
            {
                return;
            }

            var mlist = getMList(e);
            if (mlist == null)
            {
                return;
            }

            if (preserveOrder)
            {
                mlist.ForEach((o, i) => ((ICanBeOrdered)o).Order = i);
            }

            if (GraphExplorer.IsGraphModified(mlist))
            {
                e.SetModified();
            }
        };
        sb.Schema.EntityEvents <T>().Saved += (T e, SavedEventArgs args) =>
        {
            if (VirtualMList.ShouldAvoidMListType(typeof(L)))
            {
                return;
            }

            var mlist = getMList(e);

            if (mlist != null && !GraphExplorer.IsGraphModified(mlist))
            {
                return;
            }

            if (!(args.WasNew || ShouldConsiderNew(typeof(T))))
            {
                var oldElements = mlist.EmptyIfNull().Where(line => !line.IsNew);
                var query       = Database.Query <L>()
                                  .Where(p => backReference.Evaluate(p).Is(e.ToLite()));

                if (onRemove == null)
                {
                    query.Where(p => !oldElements.Contains(p)).UnsafeDelete();
                }
                else
                {
                    query.Where(p => !oldElements.Contains(p)).ToList().ForEach(line => onRemove !(line, e));
                }
            }

            if (mlist != null)
            {
                if (mlist.Any())
                {
                    if (setter == null)
                    {
                        setter = CreateSetter(backReference);
                    }

                    mlist.ForEach(line => setter !(line, e.ToLite()));
                    if (onSave == null)
                    {
                        mlist.SaveList();
                    }
                    else
                    {
                        mlist.ForEach(line => { if (GraphExplorer.IsGraphModified(line))
                                                {
                                                    onSave !(line, e);
                                                }
                                      });
                    }

                    var priv = (IMListPrivate)mlist;
                    for (int i = 0; i < mlist.Count; i++)
                    {
                        if (priv.GetRowId(i) == null)
                        {
                            priv.SetRowId(i, mlist[i].Id);
                        }
                    }
                }
                mlist.SetCleanModified(false);
            }
        };


        sb.Schema.EntityEvents <T>().PreUnsafeDelete += query =>
        {
            if (VirtualMList.ShouldAvoidMListType(typeof(L)))
            {
                return(null);
            }

            //You can do a VirtualMList to itself at the table level, but there should not be cycles inside the instances
            var toDelete = Database.Query <L>().Where(se => query.Any(e => backReference.Evaluate(se).Is(e)));
            if (defLazyDelete)
            {
                if (toDelete.Any())
                {
                    toDelete.UnsafeDelete();
                }
            }
            else
            {
                toDelete.UnsafeDelete();
            }
            return(null);
        };

        return(fi);
    }
Ejemplo n.º 2
0
    public static FluentInclude <T> WithVirtualMListInitializeOnly <T, L>(this FluentInclude <T> fi,
                                                                          Expression <Func <T, MList <L> > > mListField,
                                                                          Expression <Func <L, Lite <T>?> > backReference,
                                                                          Action <L, T>?onSave = null)
        where T : Entity
        where L : Entity
    {
        fi.SchemaBuilder.Include <L>();

        Func <T, MList <L> >  getMList = GetAccessor(mListField);
        Action <L, Lite <T> >?setter   = null;
        var sb = fi.SchemaBuilder;

        sb.Schema.EntityEvents <T>().RegisterBinding(mListField,
                                                     shouldSet: () => false,
                                                     valueExpression: (e, rowId) => Database.Query <L>().Where(line => backReference.Evaluate(line).Is(e.ToLite())).ExpandLite(line => backReference.Evaluate(line), ExpandLite.ToStringLazy).ToVirtualMListWithOrder()
                                                     );

        sb.Schema.EntityEvents <T>().Saving += (T e) =>
        {
            if (VirtualMList.ShouldAvoidMListType(typeof(L)))
            {
                return;
            }

            var mlist = getMList(e);
            if (mlist == null)
            {
                return;
            }

            if (GraphExplorer.IsGraphModified(getMList(e)))
            {
                e.SetModified();
            }
        };

        sb.Schema.EntityEvents <T>().Saved += (T e, SavedEventArgs args) =>
        {
            if (VirtualMList.ShouldAvoidMListType(typeof(L)))
            {
                return;
            }

            var mlist = getMList(e);
            if (mlist == null)
            {
                return;
            }

            if (!GraphExplorer.IsGraphModified(mlist))
            {
                return;
            }

            if (setter == null)
            {
                setter = CreateSetter(backReference);
            }

            mlist.ForEach(line => setter !(line, e.ToLite()));
            if (onSave == null)
            {
                mlist.SaveList();
            }
            else
            {
                mlist.ForEach(line =>
                {
                    if (GraphExplorer.IsGraphModified(line))
                    {
                        onSave !(line, e);
                    }
                });
            }
            var priv = (IMListPrivate)mlist;
            for (int i = 0; i < mlist.Count; i++)
            {
                if (priv.GetRowId(i) == null)
                {
                    priv.SetRowId(i, mlist[i].Id);
                }
            }
            mlist.SetCleanModified(false);
        };
        return(fi);
    }