// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

namespace FunctionalTests
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data.Entity;
    using System.Data.Entity.Core.Metadata.Edm;
    using System.Linq;
    using System.Linq.Expressions;
    using FunctionalTests.Model;
    using Xunit;

    public class DataAnnotationScenarioTests : TestBase
    {
        [Fact]
        public void Duplicate_column_order_should_not_throw_when_v1_convention_set()
        {
            var modelBuilder = new DbModelBuilder(DbModelBuilderVersion.V4_1);

            modelBuilder.Entity<Entity_10558>();

            var databaseMapping = BuildMapping(modelBuilder);

            databaseMapping.AssertValid();
        }

        [Fact]
        public void Duplicate_column_order_should_throw_when_v2_convention_set()
        {
            var modelBuilder = new DbModelBuilder(DbModelBuilderVersion.V5_0);

            modelBuilder.Entity<Entity_10558>();

            Assert.Throws<InvalidOperationException>(
                () => BuildMapping(modelBuilder))
                .ValidateMessage("DuplicateConfiguredColumnOrder", "Entity_10558");
        }

        [Fact]
        public void Non_public_annotations_are_enabled()
        {
            var modelBuilder = new DbModelBuilder();

            modelBuilder.Entity<PrivateMemberAnnotationClass>().Property(
                PrivateMemberAnnotationClass.PersonFirstNameExpr);

            var databaseMapping = BuildMapping(modelBuilder);
            databaseMapping.AssertValid();

            databaseMapping.Assert(PrivateMemberAnnotationClass.PersonFirstNameObjectExpr)
                .DbEqual("dsdsd", c => c.Name)
                .DbEqual("nvarchar", c => c.TypeName)
                .DbEqual(128, f => f.MaxLength)
                .DbEqual(true, c => c.IsPrimaryKeyColumn);
        }

        [Fact]
        public void NotMapped_should_propagate_down_inheritance_hierachy()
        {
            var modelBuilder = new DbModelBuilder();

            modelBuilder.Entity<NotMappedDerived>();

            Assert.Throws<InvalidOperationException>(
                () => BuildMapping(modelBuilder))
                .ValidateMessage("InvalidEntityType", typeof(NotMappedDerived));
        }

        [Fact]
        public void NotMapped_on_base_class_property_ignores_it()
        {
            using (var baseEntityConfiguration = new DynamicTypeDescriptionConfiguration<BaseEntity>())
            {
                var modelBuilder = new DbModelBuilder();

                baseEntityConfiguration.SetPropertyAttributes(b => b.BaseClassProperty, new NotMappedAttribute());
                baseEntityConfiguration.SetPropertyAttributes(b => b.VirtualBaseClassProperty, new NotMappedAttribute());

                modelBuilder.Entity<Unit>();
                modelBuilder.Entity<BaseEntity>();

                var databaseMapping = BuildMapping(modelBuilder);

                databaseMapping.AssertValid();

                Assert.False(
                    databaseMapping.Model.EntityTypes.SelectMany(e => e.Properties)
                        .Any(p => p.Name == "BaseClassProperty"));
                Assert.False(
                    databaseMapping.Model.EntityTypes.SelectMany(e => e.Properties)
                        .Any(p => p.Name == "VirtualBaseClassProperty"));
            }
        }

        [Fact]
        public void NotMapped_on_base_class_property_and_overriden_property_ignores_them()
        {
            using (var baseEntityConfiguration = new DynamicTypeDescriptionConfiguration<BaseEntity>())
            {
                using (var unitConfiguration = new DynamicTypeDescriptionConfiguration<Unit>())
                {
                    var modelBuilder = new DbModelBuilder();

                    unitConfiguration.SetPropertyAttributes(b => b.VirtualBaseClassProperty, new NotMappedAttribute());
                    baseEntityConfiguration.SetPropertyAttributes(b => b.VirtualBaseClassProperty, new NotMappedAttribute());

                    modelBuilder.Entity<Unit>();
                    modelBuilder.Entity<BaseEntity>();

                    var databaseMapping = BuildMapping(modelBuilder);

                    databaseMapping.AssertValid();

                    Assert.False(
                        databaseMapping.Model.EntityTypes.SelectMany(e => e.Properties)
                            .Any(p => p.Name == "VirtualBaseClassProperty"));
                }
            }
        }

        [Fact]
        public void NotMapped_on_base_class_property_discovered_through_navigation_ignores_it()
        {
            using (var abstractBaseEntityConfiguration = new DynamicTypeDescriptionConfiguration<AbstractBaseEntity>())
            {
                var modelBuilder = new DbModelBuilder();

                abstractBaseEntityConfiguration.SetPropertyAttributes(b => b.AbstractBaseClassProperty, new NotMappedAttribute());

                modelBuilder.Entity<Unit>();

                var databaseMapping = BuildMapping(modelBuilder);

                databaseMapping.AssertValid();

                Assert.True(databaseMapping.Model.EntityTypes.Any(e => e.Name == "AbstractBaseEntity"));
                Assert.False(
                    databaseMapping.Model.EntityTypes.SelectMany(e => e.Properties)
                        .Any(p => p.Name == "AbstractBaseClassProperty"));
            }
        }

        [Fact]
        public void NotMapped_on_abstract_base_class_property_ignores_it()
        {
            using (var abstractBaseEntityConfiguration = new DynamicTypeDescriptionConfiguration<AbstractBaseEntity>())
            {
                var modelBuilder = new DbModelBuilder();

                abstractBaseEntityConfiguration.SetPropertyAttributes(b => b.AbstractBaseClassProperty, new NotMappedAttribute());

                modelBuilder.Entity<AbstractBaseEntity>();
                modelBuilder.Entity<BaseEntity>();
                modelBuilder.Entity<Unit>();

                var databaseMapping = BuildMapping(modelBuilder);

                databaseMapping.AssertValid();

                Assert.False(
                    databaseMapping.Model.EntityTypes.SelectMany(e => e.Properties)
                        .Any(p => p.Name == "AbstractBaseClassProperty"));
            }
        }

        [Fact]
        public void NotMapped_on_overriden_mapped_base_class_property_throws()
        {
            using (var unitConfiguration = new DynamicTypeDescriptionConfiguration<Unit>())
            {
                var modelBuilder = new DbModelBuilder();

                unitConfiguration.SetPropertyAttributes(b => b.VirtualBaseClassProperty, new NotMappedAttribute());

                modelBuilder.Ignore<DifferentUnit>();
                modelBuilder.Entity<Unit>();
                modelBuilder.Entity<BaseEntity>();

                Assert.Throws<InvalidOperationException>(() => BuildMapping(modelBuilder))
                    .ValidateMessage(
                        "CannotIgnoreMappedBaseProperty",
                        "VirtualBaseClassProperty", "FunctionalTests.Unit",
                        "FunctionalTests.BaseEntity");
            }
        }

        [Fact]
        public void NotMapped_on_unmapped_derived_property_ignores_it()
        {
            using (var unitConfiguration = new DynamicTypeDescriptionConfiguration<Unit>())
            {
                var modelBuilder = new DbModelBuilder();

                unitConfiguration.SetPropertyAttributes(b => b.VirtualBaseClassProperty, new NotMappedAttribute());

                modelBuilder.Ignore<AbstractBaseEntity>();
                modelBuilder.Ignore<BaseEntity>();
                modelBuilder.Entity<Unit>();

                var databaseMapping = BuildMapping(modelBuilder);

                databaseMapping.AssertValid();

                Assert.False(
                    databaseMapping.Model.EntityTypes.Single().Properties.Any(
                        p => p.Name == "VirtualBaseClassProperty"));
            }
        }

        [Fact]
        public void NotMapped_on_unmapped_base_class_property_and_overriden_property_ignores_it()
        {
            using (var unitConfiguration = new DynamicTypeDescriptionConfiguration<Unit>())
            {
                using (var baseEntityConfiguration = new DynamicTypeDescriptionConfiguration<BaseEntity>())
                {
                    var modelBuilder = new DbModelBuilder();

                    baseEntityConfiguration.SetPropertyAttributes(b => b.VirtualBaseClassProperty, new NotMappedAttribute());
                    unitConfiguration.SetPropertyAttributes(b => b.VirtualBaseClassProperty, new NotMappedAttribute());

                    modelBuilder.Ignore<AbstractBaseEntity>();
                    modelBuilder.Ignore<BaseEntity>();
                    modelBuilder.Entity<Unit>();

                    var databaseMapping = BuildMapping(modelBuilder);

                    databaseMapping.AssertValid();

                    Assert.False(
                        databaseMapping.Model.EntityTypes.Single().Properties.Any(
                            p => p.Name == "VirtualBaseClassProperty"));
                }
            }
        }

        [Fact]
        public void NotMapped_on_unmapped_base_class_property_ignores_it()
        {
            using (var baseEntityConfiguration = new DynamicTypeDescriptionConfiguration<BaseEntity>())
            {
                var modelBuilder = new DbModelBuilder();

                baseEntityConfiguration.SetPropertyAttributes(b => b.VirtualBaseClassProperty, new NotMappedAttribute());

                modelBuilder.Ignore<AbstractBaseEntity>();
                modelBuilder.Ignore<BaseEntity>();
                modelBuilder.Entity<Unit>();

                var databaseMapping = BuildMapping(modelBuilder);

                databaseMapping.AssertValid();

                Assert.False(
                    databaseMapping.Model.EntityTypes.Single().Properties.Any(
                        p => p.Name == "VirtualBaseClassProperty"));
            }
        }

        [Fact]
        public void NotMapped_on_new_property_with_same_name_as_in_unmapped_base_class_ignores_it()
        {
            using (var differentUnitConfiguration = new DynamicTypeDescriptionConfiguration<DifferentUnit>())
            {
                var modelBuilder = new DbModelBuilder();

                differentUnitConfiguration.SetPropertyAttributes(b => b.VirtualBaseClassProperty, new NotMappedAttribute());

                modelBuilder.Entity<DifferentUnit>();

                var databaseMapping = BuildMapping(modelBuilder);

                databaseMapping.AssertValid();

                Assert.False(
                    databaseMapping.Model.EntityTypes.Single().Properties.Any(
                        p => p.Name == "VirtualBaseClassProperty"));
            }
        }

        [Fact]
        public void MaxLength_takes_presedence_over_StringLength()
        {
            var modelBuilder = new DbModelBuilder();

            modelBuilder.Entity<MaxLengthAnnotationClass>();

            var databaseMapping = BuildMapping(modelBuilder);
            databaseMapping.AssertValid();

            databaseMapping.Assert<MaxLengthAnnotationClass>(x => x.PersonFirstName).FacetEqual(true, a => a.IsMaxLength);
        }

        [Fact]
        public void MaxLength_with_length_takes_precedence_over_StringLength()
        {
            var modelBuilder = new DbModelBuilder();

            modelBuilder.Entity<MaxLengthWithLengthAnnotationClass>();

            var databaseMapping = BuildMapping(modelBuilder);
            databaseMapping.AssertValid();

            databaseMapping.Assert<MaxLengthWithLengthAnnotationClass>(x => x.PersonFirstName).FacetEqual(
                30,
                a =>
                a.MaxLength);
        }

        [Fact]
        public void Default_length_for_key_string_column()
        {
            using (var loginConfiguration = new DynamicTypeDescriptionConfiguration<Login>())
            {
                loginConfiguration.SetPropertyAttributes(l => l.UserName, new KeyAttribute());
                var modelBuilder = new DbModelBuilder();

                modelBuilder.Entity<Login>();
                modelBuilder.Ignore<Profile>();

                var databaseMapping = BuildMapping(modelBuilder);
                databaseMapping.AssertValid();

                databaseMapping.Assert<Login>(x => x.UserName)
                    .DbEqual("nvarchar", c => c.TypeName)
                    .DbEqual(128, f => f.MaxLength);
            }
        }

        [Fact]
        public void Key_and_column_work_together()
        {
            var modelBuilder = new DbModelBuilder();

            modelBuilder.Entity<ColumnKeyAnnotationClass>();

            var databaseMapping = BuildMapping(modelBuilder);
            databaseMapping.AssertValid();

            databaseMapping.Assert<ColumnKeyAnnotationClass>(x => x.PersonFirstName)
                .DbEqual("dsdsd", c => c.Name)
                .DbEqual("nvarchar", c => c.TypeName)
                .DbEqual(128, f => f.MaxLength)
                .DbEqual(true, c => c.IsPrimaryKeyColumn);
        }

        [Fact]
        // Regression test for Dev11 Bug 87347
        public void Key_and_column_work_together_in_an_IA()
        {
            var modelBuilder = new DbModelBuilder();

            modelBuilder.Entity<ColumnKeyAnnotationClass>();
            modelBuilder.Entity<ReferencingClass>();

            var databaseMapping = BuildMapping(modelBuilder);
            databaseMapping.AssertValid();

            databaseMapping.Assert<ColumnKeyAnnotationClass>(x => x.PersonFirstName)
                .DbEqual("dsdsd", c => c.Name)
                .DbEqual("nvarchar", c => c.TypeName)
                .DbEqual(128, f => f.MaxLength)
                .DbEqual(true, c => c.IsPrimaryKeyColumn);

            Assert.Equal(1, databaseMapping.Model.AssociationTypes.Count());
            databaseMapping.Assert<ReferencingClass>().ForeignKeyColumn("Person_PersonFirstName")
                .DbEqual("nvarchar", c => c.TypeName)
                .DbEqual(128, f => f.MaxLength);
        }

        [Fact]
        public void Key_column_and_MaxLength_work_together()
        {
            using (var entityClassConfiguration = new DynamicTypeDescriptionConfiguration<ColumnKeyAnnotationClass>())
            {
                entityClassConfiguration.SetPropertyAttributes(c => c.PersonFirstName, new MaxLengthAttribute(64));

                var modelBuilder = new DbModelBuilder();

                modelBuilder.Entity<ColumnKeyAnnotationClass>();

                var databaseMapping = BuildMapping(modelBuilder);
                databaseMapping.AssertValid();

                databaseMapping.Assert<ColumnKeyAnnotationClass>(x => x.PersonFirstName)
                    .DbEqual("dsdsd", c => c.Name)
                    .DbEqual("nvarchar", c => c.TypeName)
                    .DbEqual(64, f => f.MaxLength)
                    .DbEqual(true, c => c.IsPrimaryKeyColumn);
            }
        }

        [Fact]
        public void Key_column_and_MaxLength_work_together_in_an_IA()
        {
            using (var entityClassConfiguration = new DynamicTypeDescriptionConfiguration<ColumnKeyAnnotationClass>())
            {
                entityClassConfiguration.SetPropertyAttributes(c => c.PersonFirstName, new MaxLengthAttribute(64));

                var modelBuilder = new DbModelBuilder();

                modelBuilder.Entity<ColumnKeyAnnotationClass>();
                modelBuilder.Entity<ReferencingClass>();

                var databaseMapping = BuildMapping(modelBuilder);
                databaseMapping.AssertValid();

                databaseMapping.Assert<ColumnKeyAnnotationClass>(x => x.PersonFirstName)
                    .DbEqual("dsdsd", c => c.Name)
                    .DbEqual("nvarchar", c => c.TypeName)
                    .DbEqual(64, f => f.MaxLength)
                    .DbEqual(true, c => c.IsPrimaryKeyColumn);

                Assert.Equal(1, databaseMapping.Model.AssociationTypes.Count());
                databaseMapping.Assert<ReferencingClass>().ForeignKeyColumn("Person_PersonFirstName")
                    .DbEqual("nvarchar", c => c.TypeName)
                    .DbEqual(64, f => f.MaxLength);
            }
        }

        [Fact]
        public void Key_from_base_type_is_recognized()
        {
            var modelBuilder = new DbModelBuilder();

            modelBuilder.Entity<SRelated>();
            modelBuilder.Entity<OKeyBase>();

            var databaseMapping = BuildMapping(modelBuilder);
            databaseMapping.AssertValid();

            // one thing configured because of key property
            Assert.Equal(1, modelBuilder.ModelConfiguration.Entity(typeof(OKeyBase)).ConfiguredProperties.Count());

            // should be nothing configured on derived type
            Assert.Equal(0, modelBuilder.ModelConfiguration.Entity(typeof(DODerived)).ConfiguredProperties.Count());
        }

        [Fact]
        public void Key_on_nav_prop_is_ignored()
        {
            var modelBuilder = new DbModelBuilder();

            modelBuilder.Entity<KeyOnNavProp>();

            var databaseMapping = BuildMapping(modelBuilder);
            databaseMapping.AssertValid();

            databaseMapping.Assert<KeyOnNavProp>().DbEqual("Id", t => t.DeclaredKeyProperties.Single().Name);
        }

        [Fact]
        public void Timestamp_takes_precedence_over_MaxLength()
        {
            var modelBuilder = new DbModelBuilder();
            modelBuilder.Entity<TimestampAndMaxlen>().Ignore(x => x.NonMaxTimestamp);

            var databaseMapping = BuildMapping(modelBuilder);
            databaseMapping.AssertValid();

            databaseMapping.Assert<TimestampAndMaxlen>().DbEqual(
                "rowversion",
                t =>
                t.Properties.Single(x => x.Name == "MaxTimestamp").
                    TypeName);

            databaseMapping.Assert<TimestampAndMaxlen>().DbEqual(
                false,
                t =>
                t.Properties.Single(x => x.Name == "MaxTimestamp").IsMaxLength);

            databaseMapping.Assert<TimestampAndMaxlen>().DbEqual(
                null,
                t =>
                t.Properties.Single(x => x.Name == "MaxTimestamp").MaxLength);
        }

        [Fact]
        public void Timestamp_takes_precedence_over_MaxLength_with_value()
        {
            var modelBuilder = new DbModelBuilder();
            modelBuilder.Entity<TimestampAndMaxlen>().Ignore(x => x.MaxTimestamp);

            var databaseMapping = BuildMapping(modelBuilder);
            databaseMapping.AssertValid();

            databaseMapping.Assert<TimestampAndMaxlen>().DbEqual(
                "rowversion",
                t =>
                t.Properties.Single(x => x.Name == "NonMaxTimestamp").
                    TypeName);

            databaseMapping.Assert<TimestampAndMaxlen>().DbEqual(
                false,
                t =>
                t.Properties.Single(x => x.Name == "NonMaxTimestamp").IsMaxLength);

            databaseMapping.Assert<TimestampAndMaxlen>().DbEqual(
                null,
                t =>
                t.Properties.Single(x => x.Name == "NonMaxTimestamp").MaxLength);
        }

        [Fact]
        public void Annotation_in_derived_class_when_base_class_processed_after_derived_class()
        {
            var modelBuilder = new AdventureWorksModelBuilder();

            modelBuilder.Entity<StyledProduct>();
            modelBuilder.Entity<Product>();

            var databaseMapping = BuildMapping(modelBuilder);

            databaseMapping.Assert<StyledProduct>(s => s.Style).FacetEqual(150, f => f.MaxLength);
        }

        [Fact]
        public void Required_and_ForeignKey_to_Required()
        {
            using (var loginConfiguration = new DynamicTypeDescriptionConfiguration<Login>())
            {
                using (var profileConfiguration = new DynamicTypeDescriptionConfiguration<Profile>())
                {
                    loginConfiguration.SetPropertyAttributes(
                        l => l.Profile, new RequiredAttribute(),
                        new ForeignKeyAttribute("LoginId"));
                    profileConfiguration.SetPropertyAttributes(p => p.User, new RequiredAttribute());

                    var modelBuilder = new DbModelBuilder();

                    modelBuilder.Entity<Login>();
                    modelBuilder.Entity<Profile>();

                    Assert.Throws<InvalidOperationException>(
                        () => BuildMapping(modelBuilder))
                        .ValidateMessage("UnableToDeterminePrincipal", typeof(Login), typeof(Profile));
                }
            }
        }

        [Fact]
        // Regression test for Dev11 Bug 94993
        public void Required_to_Required_and_ForeignKey()
        {
            using (var loginConfiguration = new DynamicTypeDescriptionConfiguration<Login>())
            {
                using (var profileConfiguration = new DynamicTypeDescriptionConfiguration<Profile>())
                {
                    loginConfiguration.SetPropertyAttributes(l => l.Profile, new RequiredAttribute());
                    profileConfiguration.SetPropertyAttributes(
                        p => p.User, new RequiredAttribute(),
                        new ForeignKeyAttribute("ProfileId"));

                    var modelBuilder = new DbModelBuilder();

                    modelBuilder.Entity<Login>();
                    modelBuilder.Entity<Profile>();

                    Assert.Throws<InvalidOperationException>(
                        () => BuildMapping(modelBuilder))
                        .ValidateMessage("UnableToDeterminePrincipal", typeof(Login), typeof(Profile));
                }
            }
        }

        [Fact]
        public void Required_and_ForeignKey_to_Required_and_ForeignKey()
        {
            using (var loginConfiguration = new DynamicTypeDescriptionConfiguration<Login>())
            {
                using (var profileConfiguration = new DynamicTypeDescriptionConfiguration<Profile>())
                {
                    loginConfiguration.SetPropertyAttributes(
                        l => l.Profile, new RequiredAttribute(),
                        new ForeignKeyAttribute("LoginId"));
                    profileConfiguration.SetPropertyAttributes(
                        p => p.User, new RequiredAttribute(),
                        new ForeignKeyAttribute("ProfileId"));

                    var modelBuilder = new DbModelBuilder();

                    modelBuilder.Entity<Login>();
                    modelBuilder.Entity<Profile>();

                    Assert.Throws<InvalidOperationException>(
                        () => BuildMapping(modelBuilder))
                        .ValidateMessage("UnableToDeterminePrincipal", typeof(Login), typeof(Profile));
                }
            }
        }

        [Fact]
        public void ForeignKey_to_nothing()
        {
            using (var loginConfiguration = new DynamicTypeDescriptionConfiguration<Login>())
            {
                using (new DynamicTypeDescriptionConfiguration<Profile>())
                {
                    loginConfiguration.SetPropertyAttributes(l => l.Profile, new ForeignKeyAttribute("LoginId"));

                    var modelBuilder = new DbModelBuilder();

                    modelBuilder.Entity<Login>();
                    modelBuilder.Entity<Profile>();

                    Assert.Throws<InvalidOperationException>(
                        () => BuildMapping(modelBuilder))
                        .ValidateMessage("UnableToDeterminePrincipal", typeof(Profile), typeof(Login));
                }
            }
        }

        [Fact]
        public void Required_and_ForeignKey_to_nothing()
        {
            using (var loginConfiguration = new DynamicTypeDescriptionConfiguration<Login>())
            {
                using (new DynamicTypeDescriptionConfiguration<Profile>())
                {
                    loginConfiguration.SetPropertyAttributes(l => l.Profile, new ForeignKeyAttribute("LoginId"));

                    var modelBuilder = new DbModelBuilder();

                    modelBuilder.Entity<Login>();
                    modelBuilder.Entity<Profile>();

                    Assert.Throws<InvalidOperationException>(
                        () => BuildMapping(modelBuilder))
                        .ValidateMessage("UnableToDeterminePrincipal", typeof(Profile), typeof(Login));
                }
            }
        }

        [Fact]
        public void Nothing_to_ForeignKey()
        {
            using (var loginConfiguration = new DynamicTypeDescriptionConfiguration<Login>())
            {
                using (var profileConfiguration = new DynamicTypeDescriptionConfiguration<Profile>())
                {
                    loginConfiguration.SetPropertyAttributes(l => l.Profile);
                    profileConfiguration.SetPropertyAttributes(p => p.User, new ForeignKeyAttribute("ProfileId"));

                    var modelBuilder = new DbModelBuilder();

                    modelBuilder.Entity<Login>();
                    modelBuilder.Entity<Profile>();

                    Assert.Throws<InvalidOperationException>(
                        () => BuildMapping(modelBuilder))
                        .ValidateMessage("UnableToDeterminePrincipal", typeof(Profile), typeof(Login));
                }
            }
        }

        [Fact]
        public void Nothing_to_Required_and_ForeignKey()
        {
            using (var loginConfiguration = new DynamicTypeDescriptionConfiguration<Login>())
            {
                using (var profileConfiguration = new DynamicTypeDescriptionConfiguration<Profile>())
                {
                    loginConfiguration.SetPropertyAttributes(l => l.Profile);
                    profileConfiguration.SetPropertyAttributes(
                        p => p.User, new RequiredAttribute(),
                        new ForeignKeyAttribute("ProfileId"));

                    var modelBuilder = new DbModelBuilder();

                    modelBuilder.Entity<Login>();
                    modelBuilder.Entity<Profile>();

                    var databaseMapping = BuildMapping(modelBuilder);
                    databaseMapping.AssertValid();

                    var association = databaseMapping.Model.AssociationTypes.Single();
                    Assert.Equal("Profile", association.SourceEnd.GetEntityType().Name);
                    Assert.Equal(RelationshipMultiplicity.ZeroOrOne, association.SourceEnd.RelationshipMultiplicity);
                    Assert.Equal("Login", association.TargetEnd.GetEntityType().Name);
                    Assert.Equal(RelationshipMultiplicity.One, association.TargetEnd.RelationshipMultiplicity);
                    Assert.Equal("Profile", association.Constraint.ToRole.GetEntityType().Name);
                }
            }
        }

        [Fact]
        public void ForeignKey_to_ForeignKey()
        {
            using (var loginConfiguration = new DynamicTypeDescriptionConfiguration<Login>())
            {
                using (var profileConfiguration = new DynamicTypeDescriptionConfiguration<Profile>())
                {
                    loginConfiguration.SetPropertyAttributes(l => l.Profile, new ForeignKeyAttribute("LoginId"));
                    profileConfiguration.SetPropertyAttributes(p => p.User, new ForeignKeyAttribute("ProfileId"));

                    var modelBuilder = new DbModelBuilder();

                    modelBuilder.Entity<Login>();
                    modelBuilder.Entity<Profile>();

                    Assert.Throws<InvalidOperationException>(
                        () => BuildMapping(modelBuilder))
                        .ValidateMessage("UnableToDeterminePrincipal", typeof(Profile), typeof(Login));
                }
            }
        }

        [Fact]
        public void TableNameAttribute_affects_only_base_in_TPT()
        {
            var modelBuilder = new AdventureWorksModelBuilder();

            modelBuilder.Entity<TNAttrBase>()
                .Map<TNAttrDerived>(mc => mc.ToTable("B"));

            var databaseMapping = BuildMapping(modelBuilder);

            databaseMapping.Assert<TNAttrBase>("A");
            databaseMapping.Assert<TNAttrDerived>("B");
        }

        [Fact]
        public void TableNameAttribute_affects_table_name_in_TPH()
        {
            var modelBuilder = new AdventureWorksModelBuilder();

            modelBuilder.Entity<TNAttrBase>()
                .Map(mc => mc.Requires("disc").HasValue("A"))
                .Map<TNAttrDerived>(mc => mc.Requires("disc").HasValue("B"));

            var databaseMapping = BuildMapping(modelBuilder);

            databaseMapping.Assert<TNAttrBase>("A");
            databaseMapping.AssertMapping<TNAttrBase>("A", false).HasColumnCondition("disc", "A");
            databaseMapping.Assert<TNAttrDerived>("A");
            databaseMapping.AssertMapping<TNAttrDerived>("A").HasColumnCondition("disc", "B");
        }
    }

    #region Fixtures

    public class MaxLengthAnnotationClass
    {
        public int Id { get; set; }

        [StringLength(500)]
        [MaxLength]
        public string PersonFirstName { get; set; }
    }

    public class MaxLengthWithLengthAnnotationClass
    {
        public int Id { get; set; }

        [StringLength(500)]
        [MaxLength(30)]
        public string PersonFirstName { get; set; }
    }

    public class ColumnKeyAnnotationClass
    {
        [Key]
        [Column("dsdsd", Order = 1, TypeName = "nvarchar")]
        public string PersonFirstName { get; set; }
    }

    public class ReferencingClass
    {
        public int Id { get; set; }
        public ColumnKeyAnnotationClass Person { get; set; }
    }

    public class DASimple
    {
        public int Id { get; set; }
    }

    public class KeyOnNavProp
    {
        public int Id { get; set; }

        [Key]
        public ICollection<DASimple> Simples { get; set; }

        [Key]
        public DASimple SpecialSimple { get; set; }
    }

    public class SRelated
    {
        public int SRelatedId { get; set; }
        public ICollection<DODerived> DADeriveds { get; set; }
    }

    public class OKeyBase
    {
        [Key]
        public int OrderLineNo { get; set; }

        public int Quantity { get; set; }
    }

    public class DODerived : OKeyBase
    {
        public SRelated DARelated { get; set; }
        public string Special { get; set; }
    }

    public class TimestampAndMaxlen
    {
        public int Id { get; set; }

        [MaxLength]
        [Timestamp]
        public byte[] MaxTimestamp { get; set; }

        [MaxLength(100)]
        [Timestamp]
        public byte[] NonMaxTimestamp { get; set; }
    }

    public class Login
    {
        public int LoginId { get; set; }
        public string UserName { get; set; }
        public virtual Profile Profile { get; set; }
    }

    public class Profile
    {
        public int ProfileId { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
        public virtual Login User { get; set; }
    }

    [Table("A")]
    public class TNAttrBase
    {
        public int Id { get; set; }
        public string BaseData { get; set; }
    }

    public class TNAttrDerived : TNAttrBase
    {
        public string DerivedData { get; set; }
    }

    [NotMapped]
    public class NotMappedBase
    {
        public int Id { get; set; }
    }

    public class NotMappedDerived : NotMappedBase
    {
    }

    public class PrivateMemberAnnotationClass
    {
        public static Expression<Func<PrivateMemberAnnotationClass, string>> PersonFirstNameExpr =
            p => p.PersonFirstName;

        public static Expression<Func<PrivateMemberAnnotationClass, object>> PersonFirstNameObjectExpr =
            p => p.PersonFirstName;

        [Key]
        [Column("dsdsd", Order = 1, TypeName = "nvarchar")]
        private string PersonFirstName { get; set; }
    }

    public class Entity_10558
    {
        [Key]
        [Column(Order = 1)]
        public int Key1 { get; set; }

        [Key]
        [Column(Order = 1)]
        public int Key2 { get; set; }

        public string Name { get; set; }
    }

    #endregion

    #region Bug324763

    namespace Bug324763
    {
        using System.Data.Entity.ModelConfiguration.Conventions;

        public class Product
        {
            [Timestamp]
            public byte[] Version { get; set; }

            [Key]
            [Column(Order = 0)]
            public int ProductId { get; set; }

            [Key]
            [Column(Order = 1)]
            [MaxLength(128)]
            public string Sku { get; set; }

            [Required]
            [StringLength(15)]
            public string Name { get; set; }

            public byte[] Image { get; set; }

            [InverseProperty("Product")]
            public ICollection<OrderLine> OrderLines { get; set; }
        }

        public class OrderLine
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public int Id { get; set; }

            public int OrderId { get; set; }

            [NotMapped]
            public short Quantity { get; set; }

            public decimal Price { get; set; }
            public decimal Total { get; set; }
            public bool? IsShipped { get; set; }

            [ForeignKey("Product")]
            [Column(Order = 0)]
            public int ProductIdFk { get; set; }

            [ForeignKey("Product")]
            [Column(Order = 1)]
            public string ProductSkuFk { get; set; }

            [MaxLength(128)]
            public string Sku { get; set; }

            [ConcurrencyCheck]
            public int EngineSupplierId { get; set; }

            public Product Product { get; set; }
        }

        public class Test324763 : FunctionalTestBase
        {
            [Fact]
            public void Repro324763_Build_Is_Not_Idempotent()
            {
                var modelBuilder = new DbModelBuilder();
                modelBuilder.Entity<Product>();
                modelBuilder.Entity<OrderLine>();

                ValidateBuildIsIdempotent(modelBuilder);
            }

            private void ValidateBuildIsIdempotent(DbModelBuilder modelBuilder)
            {
                var mapping1 = BuildMapping(modelBuilder);
                var mapping2 = BuildMapping(modelBuilder);
                Assert.True(mapping1.EdmxIsEqualTo(mapping2));
            }

            [Fact]
            public void Repro324763_Build_Is_Not_Idempotent_Inverse()
            {
                var modelBuilder = new DbModelBuilder();
                modelBuilder.Conventions.Remove<AssociationInverseDiscoveryConvention>();
                modelBuilder.Entity<Product>();
                modelBuilder.Entity<OrderLine>();

                ValidateBuildIsIdempotent(modelBuilder);
            }
        }
    }

    #endregion
}
