예제 #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) == 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) == 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) == 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) == 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) == 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) == 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);
        }
예제 #2
0
        public static int BulkInsertTable <T>(IEnumerable <T> entities,
                                              SqlBulkCopyOptions copyOptions = SqlBulkCopyOptions.Default,
                                              bool preSaving       = true,
                                              bool validateFirst   = true,
                                              bool disableIdentity = false,
                                              int?timeout          = null,
                                              string?message       = null)
            where T : Entity
        {
            using (HeavyProfiler.Log(nameof(BulkInsertTable), () => typeof(T).TypeName()))
            {
                if (message != null)
                {
                    return(SafeConsole.WaitRows(message == "auto" ? $"BulkInsering {entities.Count()} {typeof(T).TypeName()}" : message,
                                                () => BulkInsertTable(entities, copyOptions, preSaving, validateFirst, disableIdentity, timeout, message: null)));
                }

                if (disableIdentity)
                {
                    copyOptions |= SqlBulkCopyOptions.KeepIdentity;
                }

                if (copyOptions.HasFlag(SqlBulkCopyOptions.UseInternalTransaction))
                {
                    throw new InvalidOperationException("BulkInsertDisableIdentity not compatible with UseInternalTransaction");
                }

                var list = entities.ToList();

                if (preSaving)
                {
                    Saver.PreSaving(() => GraphExplorer.FromRoots(list));
                }

                if (validateFirst)
                {
                    Validate <T>(list);
                }

                var  t = Schema.Current.Table <T>();
                bool disableIdentityBehaviour = copyOptions.HasFlag(SqlBulkCopyOptions.KeepIdentity);

                DataTable dt      = new DataTable();
                var       columns = t.Columns.Values.Where(c => !(c is SystemVersionedInfo.SqlServerPeriodColumn) && (disableIdentityBehaviour || !c.IdentityBehaviour)).ToList();
                foreach (var c in columns)
                {
                    dt.Columns.Add(new DataColumn(c.Name, ConvertType(c.Type)));
                }

                using (disableIdentityBehaviour ? Administrator.DisableIdentity(t, behaviourOnly: true) : null)
                {
                    foreach (var e in list)
                    {
                        if (!e.IsNew)
                        {
                            throw new InvalidOperationException("Entites should be new");
                        }
                        t.SetToStrField(e);
                        dt.Rows.Add(t.BulkInsertDataRow(e));
                    }
                }

                using (Transaction tr = new Transaction())
                {
                    Schema.Current.OnPreBulkInsert(typeof(T), inMListTable: false);

                    Executor.BulkCopy(dt, columns, t.Name, copyOptions, timeout);

                    foreach (var item in list)
                    {
                        item.SetNotModified();
                    }

                    return(tr.Commit(list.Count));
                }
            }
        }