Example #1
0
 public static string GetComputeHashIdSelector(IsSubtypeOfInfo conceptInfo)
 {
     return("item => " +
            (conceptInfo.ImplementationName == ""
             ? "item.ID"
             : "DomUtility.GetSubtypeImplementationId(item.ID, " + DomUtility.GetSubtypeImplementationHash(conceptInfo.ImplementationName) + ")"));
 }
        private string CreateComputedColumnSnippet()
        {
            return(string.Format(
                       @"ALTER TABLE {0}.{1} ADD {2}
	AS CONVERT(UNIQUEIDENTIFIER, CONVERT(BINARY(4), CONVERT(INT, CONVERT(BINARY(4), ID)) ^ {3}) + SUBSTRING(CONVERT(BINARY(16), ID), 5, 12))
	PERSISTED NOT NULL;
CREATE UNIQUE INDEX IX_{1}_{2} ON {0}.{1}({2});",
                       Subtype.Module.Name,
                       Subtype.Name,
                       GetComputedColumnName(),
                       DomUtility.GetSubtypeImplementationHash(ImplementationName)));
        }
Example #3
0
        public void GetSubtypeImplementationId()
        {
            var testGuids = new Guid[] {
                new Guid("60CFE21A-1C36-45A0-9D57-DD635551B33B"),
                new Guid("12345678-2345-3456-4567-567890123456"),
                new Guid("FEDCBFED-CBFE-DCBF-EDCB-FEDCBFEDCBFE"),
                new Guid("00000000-0000-0000-0000-000000000000"),
                new Guid("11111111-1111-1111-1111-111111111111"),
                new Guid("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"),
            };

            var testHashes = new int[] {
                0,
                1,
                -1,
                123456,
                -123456,
                123456789,
                -123456789,
                1234567890,
                -1234567890,
                2147483647,
                -2147483647,
                -2147483648
            };

            using (var container = new RhetosTestContainer())
            {
                var sqlExecuter = container.Resolve <ISqlExecuter>();

                foreach (var guid in testGuids)
                {
                    foreach (var hash in testHashes)
                    {
                        Guid csId = DomUtility.GetSubtypeImplementationId(guid, hash);

                        var sql = string.Format(
                            @"SELECT ID = CONVERT(UNIQUEIDENTIFIER, CONVERT(BINARY(4), CONVERT(INT, CONVERT(BINARY(4), {1})) ^ {0}) + SUBSTRING(CONVERT(BINARY(16), {1}), 5, 12))",
                            hash,
                            "CONVERT(UNIQUEIDENTIFIER, '" + guid.ToString().ToUpper() + "')");

                        Guid sqlId = Guid.Empty;
                        sqlExecuter.ExecuteReader(sql, reader => { sqlId = reader.GetGuid(0); });

                        Assert.AreEqual(csId, sqlId);
                    }
                }
            }
        }
 /// <summary>
 /// Same subtype may implement same supertype multiple time. Since ID of the supertype is usually same as subtype's ID,
 /// that might result with multiple supertype records with the same ID. To avoid duplicate IDs and still keep the
 /// deterministic ID values, the supertype's ID is XORed by a hash code taken from the ImplementationName.
 /// </summary>
 private string GetSubtypeImplementationIdSnippet()
 {
     if (IsSubtypeOf.ImplementationName == "")
     {
         return("");
     }
     else if (IsSubtypeOf.SupportsPersistedSubtypeImplementationColum())
     {
         return(",\r\n    SubtypeImplementationID = " + PersistedSubtypeImplementationIdInfo.GetComputedColumnName(IsSubtypeOf.ImplementationName));
     }
     else
     {
         int hash = DomUtility.GetSubtypeImplementationHash(IsSubtypeOf.ImplementationName);
         return(",\r\n    SubtypeImplementationID = CONVERT(UNIQUEIDENTIFIER, CONVERT(BINARY(4), CONVERT(INT, CONVERT(BINARY(4), ID)) ^ " + hash + ") + SUBSTRING(CONVERT(BINARY(16), ID), 5, 12))");
     }
 }
Example #5
0
        public void FilterSubtype()
        {
            using (var container = new RhetosTestContainer())
            {
                var repository = container.Resolve <Common.DomRepository>();
                repository.TestPolymorphic.ComplexImplementationData.Delete(repository.TestPolymorphic.ComplexImplementationData.Load());
                repository.TestPolymorphic.ComplexImplementationSql.Delete(repository.TestPolymorphic.ComplexImplementationSql.Load());

                Func <Guid, string, Guid> hash = (id, implementation) =>
                                                 DomUtility.GetSubtypeImplementationId(id,
                                                                                       DomUtility.GetSubtypeImplementationHash(implementation));

                var d = new TestPolymorphic.ComplexImplementationData {
                    a = "d", ID = Guid.NewGuid()
                };
                var sx = new TestPolymorphic.ComplexImplementationSql {
                    s = "sx", ID = Guid.NewGuid()
                };
                var sy = new TestPolymorphic.ComplexImplementationSql {
                    s = "sy", ID = Guid.NewGuid()
                };
                // Automatic update of _Materialized entity will not work if AlternativeId does not match ID hashed with implementation name.
                sx.AlternativeId = hash(sx.ID, "sql2");
                sy.AlternativeId = hash(sy.ID, "sql2");

                repository.TestPolymorphic.ComplexImplementationData.Insert(new[] { d });
                repository.TestPolymorphic.ComplexImplementationSql.Insert(new[] { sx, sy });

                Assert.AreEqual("abc1, abc2, sx3, sx4, sy3, sy4",
                                TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.Name1));

                Action <string, IEnumerable <Guid>, string, string> test =
                    (expected, ids, subtype, implementationName) => Assert.AreEqual(expected,
                                                                                    TestUtility.DumpSorted(
                                                                                        repository.TestPolymorphic.ComplexBase.Filter(new FilterSubtype {
                    Ids = ids, Subtype = subtype, ImplementationName = implementationName
                }),
                                                                                        item => item.Name1));

                // Testing filter:

                test("abc1", new[] { d.ID }, "TestPolymorphic.ComplexImplementationQuery", null);
                test("abc1", new[] { d.ID }, "TestPolymorphic.ComplexImplementationQuery", "");
                test("abc2", new[] { hash(d.ID, "q2") }, "TestPolymorphic.ComplexImplementationQuery", "q2");
                test("", new[] { d.ID }, "TestPolymorphic.ComplexImplementationSql", null);
                test("sx3", new[] { sx.ID }, "TestPolymorphic.ComplexImplementationSql", null);
                test("sx4", new[] { sx.AlternativeId.Value }, "TestPolymorphic.ComplexImplementationSql", "sql2");

                TestUtility.ShouldFail <Rhetos.ClientException>(() => repository.TestPolymorphic.ComplexBase.Filter(new FilterSubtype {
                    Ids     = new Guid[] {},
                    Subtype = "nonexisting", ImplementationName = ""
                }), "nonexisting");

                // Testing update of materialized data (it uses the Subtype filter):

                Assert.AreEqual(
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.ID),
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase_Materialized.Query(), item => item.ID));

                sx.s = "sxx";
                repository.TestPolymorphic.ComplexImplementationSql.Update(new[] { sx });
                Assert.AreEqual("abc1, abc2, sxx3, sxx4, sy3, sy4",
                                TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.Name1));
                Assert.AreEqual(
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.ID),
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase_Materialized.Query(), item => item.ID));

                repository.TestPolymorphic.ComplexImplementationSql.Delete(new[] { sx });
                Assert.AreEqual("abc1, abc2, sy3, sy4",
                                TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.Name1));
                Assert.AreEqual(
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.ID),
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase_Materialized.Query(), item => item.ID));

                repository.TestPolymorphic.ComplexImplementationData.Delete(new[] { d });
                Assert.AreEqual("sy3, sy4",
                                TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.Name1));
                Assert.AreEqual(
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.ID),
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase_Materialized.Query(), item => item.ID));

                repository.TestPolymorphic.ComplexImplementationSql.Delete(new[] { sy });
                Assert.AreEqual("",
                                TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.Name1));
                Assert.AreEqual(
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase.Query(), item => item.ID),
                    TestUtility.DumpSorted(repository.TestPolymorphic.ComplexBase_Materialized.Query(), item => item.ID));
            }
        }
Example #6
0
        public void MultipleImplementations()
        {
            foreach (bool useDatabaseNullSemantics in new[] { false, true })
            {
                using (var container = new RhetosTestContainer())
                {
                    container.SetUseDatabaseNullSemantics(useDatabaseNullSemantics);
                    var repository = container.Resolve <Common.DomRepository>();

                    // Initialize data:

                    repository.TestPolymorphic.MultipleImplementations.Delete(repository.TestPolymorphic.MultipleImplementations.Query());

                    var mi1 = new TestPolymorphic.MultipleImplementations {
                        Name1 = "abc", Name2 = "123"
                    };
                    var mi2 = new TestPolymorphic.MultipleImplementations {
                        Name1 = "def", Name2 = "456"
                    };

                    repository.TestPolymorphic.MultipleImplementations.Insert(new[] { mi1, mi2 });

                    // Testing unions:

                    var base1 = repository.TestPolymorphic.Base1.Load();
                    Assert.AreEqual("abc, cba, def, fed", TestUtility.DumpSorted(base1, item => item.Name1));

                    var base2 = repository.TestPolymorphic.Base2.Load();
                    Assert.AreEqual("123, 321, 456, 654", TestUtility.DumpSorted(base2, item => item.Name2));

                    var base3 = repository.TestPolymorphic.Base3.Load();
                    Assert.AreEqual("abc-3, def-3", TestUtility.DumpSorted(base3, item => item.Name1));

                    // Testing specific implementation ID uniqueness:

                    var base1IDs = base1.Select(item => item.ID).ToList();
                    Assert.AreEqual(base1IDs.Count, base1IDs.Distinct().Count());

                    // Testing specific implementation ID stability:

                    var secondRead = repository.TestPolymorphic.Base1.Query();
                    Assert.AreEqual(
                        TestUtility.DumpSorted(base1IDs),
                        TestUtility.DumpSorted(secondRead, item => item.ID));

                    // Testing querying by specific implementation subtype:

                    Assert.AreEqual(
                        "abc-, cba-abc, def-, fed-def",
                        TestUtility.DumpSorted(repository.TestPolymorphic.Base1.Query()
                                               .ToList()
                                               .Select(item => item.Name1 + "-" + item.MultipleImplementationsReverse?.Name1)));

                    // Testing C# implementation:

                    int implementationHash = DomUtility.GetSubtypeImplementationHash("Reverse");

                    var expected = new[] {
                        new TestPolymorphic.Base1 {
                            ID = DomUtility.GetSubtypeImplementationId(mi1.ID, implementationHash),
                            MultipleImplementationsReverseID = mi1.ID
                        },
                        new TestPolymorphic.Base1 {
                            ID = DomUtility.GetSubtypeImplementationId(mi2.ID, implementationHash),
                            MultipleImplementationsReverseID = mi2.ID
                        },
                    };
                    var actual = base1.Where(item => item.MultipleImplementationsReverseID != null);
                    Assert.AreEqual(
                        TestUtility.DumpSorted(expected, item => item.MultipleImplementationsReverseID.ToString() + "/" + item.ID.ToString()),
                        TestUtility.DumpSorted(actual, item => item.MultipleImplementationsReverseID.ToString() + "/" + item.ID.ToString()));

                    // Testing persisted IDs for specific implementation subtype:

                    Assert.AreEqual(
                        TestUtility.DumpSorted(base1IDs),
                        TestUtility.DumpSorted(repository.TestPolymorphic.Base1_Materialized.Query().Select(item => item.ID)));
                }
            }
        }
Example #7
0
        public IEnumerable <IConceptInfo> CreateNewConcepts(IsSubtypeOfInfo conceptInfo, IDslModel existingConcepts)
        {
            var newConcepts = new List <IConceptInfo>();

            // Add a subtype reference (for each subtype) to the supertype data structure:

            var subtypeReference = new ReferencePropertyInfo
            {
                DataStructure = conceptInfo.Supertype,
                Referenced    = conceptInfo.Subtype,
                Name          = conceptInfo.GetSubtypeReferenceName()
            };

            newConcepts.Add(subtypeReference);
            newConcepts.Add(new PolymorphicPropertyInfo {
                Property = subtypeReference, SubtypeReference = conceptInfo.Subtype.GetKeyProperties()
            });

            // Append subtype implementation to the supertype union:

            newConcepts.Add(new SubtypeExtendPolymorphicInfo
            {
                IsSubtypeOf = conceptInfo,
                SubtypeImplementationView = conceptInfo.GetImplementationViewPrototype(),
                PolymorphicUnionView      = conceptInfo.Supertype.GetUnionViewPrototype()
            });

            var filterBySubtypePrototype = new FilterByInfo {
                Source = conceptInfo.Supertype, Parameter = "Rhetos.Dom.DefaultConcepts.FilterSubtype"
            };

            newConcepts.Add(new SubtypeExtendFilterInfo
            {
                IsSubtypeOf     = conceptInfo,
                FilterBySubtype = filterBySubtypePrototype
            });

            // Add metadata for supertype computation (union):

            string hashId = conceptInfo.ImplementationName == "" ? "item.ID"
                : "DomUtility.GetSubtypeImplementationId(item.ID, " + DomUtility.GetSubtypeImplementationHash(conceptInfo.ImplementationName) + ")";

            foreach (DataStructureInfo dependsOn in DslUtility.GetBaseChangesOnDependency(conceptInfo.Subtype, existingConcepts))
            {
                newConcepts.Add(new ChangesOnChangedItemsInfo
                {
                    Computation   = conceptInfo.Supertype,
                    DependsOn     = dependsOn,
                    FilterType    = "Rhetos.Dom.DefaultConcepts.FilterSubtype",
                    FilterFormula = @"changedItems => new Rhetos.Dom.DefaultConcepts.FilterSubtype
                        {
                            Ids = changedItems.Select(item => " + hashId + @").ToArray(),
                            Subtype = " + CsUtility.QuotedString(conceptInfo.Subtype.Module.Name + "." + conceptInfo.Subtype.Name) + @",
                            ImplementationName = " + CsUtility.QuotedString(conceptInfo.ImplementationName) + @"
                        }"
                });
            }

            // Add metadata for subtype implementation:

            PersistedSubtypeImplementationIdInfo subtypeImplementationColumn = null;

            if (conceptInfo.SupportsPersistedSubtypeImplementationColum())
            {
                subtypeImplementationColumn = new PersistedSubtypeImplementationIdInfo {
                    Subtype = conceptInfo.Subtype, ImplementationName = conceptInfo.ImplementationName
                };
                newConcepts.Add(subtypeImplementationColumn);
            }

            // Automatic interface implementation:

            var implementationView = (SqlViewInfo)existingConcepts.FindByKey(conceptInfo.GetImplementationViewPrototype().GetKey());

            if (implementationView == null)
            {
                implementationView = new ExtensibleSubtypeSqlViewInfo {
                    IsSubtypeOf = conceptInfo
                };
                newConcepts.Add(implementationView);

                if (subtypeImplementationColumn != null)
                {
                    newConcepts.Add(new SqlDependsOnSqlObjectInfo
                    {
                        // The subtype implementation view will use the PersistedSubtypeImplementationColumn.
                        DependsOn = subtypeImplementationColumn.GetSqlObjectPrototype(),
                        Dependent = implementationView
                    });
                }
            }

            // Redirect the developer-provided SQL dependencies from the "Is" concept to the implementation view:

            newConcepts.AddRange(existingConcepts.FindByReference <SqlDependsOnDataStructureInfo>(dep => dep.Dependent, conceptInfo)
                                 .Select(dep => new SqlDependsOnDataStructureInfo {
                Dependent = implementationView, DependsOn = dep.DependsOn
            }));

            newConcepts.AddRange(existingConcepts.FindByReference <SqlDependsOnModuleInfo>(dep => dep.Dependent, conceptInfo)
                                 .Select(dep => new SqlDependsOnModuleInfo {
                Dependent = implementationView, DependsOn = dep.DependsOn
            }));

            newConcepts.AddRange(existingConcepts.FindByReference <SqlDependsOnPropertyInfo>(dep => dep.Dependent, conceptInfo)
                                 .Select(dep => new SqlDependsOnPropertyInfo {
                Dependent = implementationView, DependsOn = dep.DependsOn
            }));

            newConcepts.AddRange(existingConcepts.FindByReference <SqlDependsOnSqlFunctionInfo>(dep => dep.Dependent, conceptInfo)
                                 .Select(dep => new SqlDependsOnSqlFunctionInfo {
                Dependent = implementationView, DependsOn = dep.DependsOn
            }));

            newConcepts.AddRange(existingConcepts.FindByReference <SqlDependsOnSqlIndexInfo>(dep => dep.Dependent, conceptInfo)
                                 .Select(dep => new SqlDependsOnSqlIndexInfo {
                Dependent = implementationView, DependsOn = dep.DependsOn
            }));

            newConcepts.AddRange(existingConcepts.FindByReference <SqlDependsOnSqlObjectInfo>(dep => dep.Dependent, conceptInfo)
                                 .Select(dep => new SqlDependsOnSqlObjectInfo {
                Dependent = implementationView, DependsOn = dep.DependsOn
            }));

            newConcepts.AddRange(existingConcepts.FindByReference <SqlDependsOnSqlViewInfo>(dep => dep.Dependent, conceptInfo)
                                 .Select(dep => new SqlDependsOnSqlViewInfo {
                Dependent = implementationView, DependsOn = dep.DependsOn
            }));

            return(newConcepts);
        }