├── src ├── EntityFramework.Metadata │ ├── NuSpec │ │ └── install.ps1 │ ├── packages.config │ ├── Exceptions │ │ └── ParentNotMappedYetException.cs │ ├── Mappers │ │ ├── TphData.cs │ │ ├── CodeFirstMapper.cs │ │ ├── DbFirstMapper.cs │ │ └── MapperBase.cs │ ├── App.config │ ├── IDbMapping.cs │ ├── EntityFramework.Metadata.nuspec │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Extensions │ │ ├── MappingApiExtensions.cs │ │ └── TypeExtensions.cs │ ├── EfMap.cs │ ├── IEntityMap.cs │ ├── IPropertyMap.cs │ ├── EntityFramework.Metadata.Net45.csproj │ ├── EntityFramework.Metadata.csproj │ └── Mappings │ │ ├── DbMapping.cs │ │ ├── PropertyMap.cs │ │ └── EntityMap.cs ├── EntityFramework.Metadata.Test │ ├── CodeFirst │ │ ├── Domain │ │ │ ├── Entity.cs │ │ │ ├── EntityWithTypedId.cs │ │ │ ├── EntityWithMappedPk.cs │ │ │ ├── ComplexTypes │ │ │ │ ├── Contact.cs │ │ │ │ └── Address.cs │ │ │ ├── Foo.cs │ │ │ ├── MeteringPoint.cs │ │ │ ├── PageTranslations.cs │ │ │ ├── Reading.cs │ │ │ ├── TestUser.cs │ │ │ ├── House.cs │ │ │ ├── Company.cs │ │ │ ├── Page.cs │ │ │ ├── Employee.cs │ │ │ └── Contract.cs │ │ ├── TestBase.cs │ │ ├── TestContext.cs │ │ ├── TestHelper.cs │ │ ├── TphTest.cs │ │ └── MappingTest.cs │ ├── packages.config │ ├── DbFirst │ │ ├── TestModel.cs │ │ ├── TestModel1.cs │ │ ├── TestModel.Designer.cs │ │ ├── TestModel1.Designer.cs │ │ ├── Post.cs │ │ ├── Blogs.cs │ │ ├── TestModel.edmx.diagram │ │ ├── TestModel.Context.cs │ │ ├── TestModel.Context1.cs │ │ ├── MappingTest.cs │ │ ├── TestModel.edmx │ │ └── TestModel.Context.tt │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── App.config │ ├── EntityFramework.Metadata.Test.csproj │ └── EntityFramework.Metadata.Test.Net45.csproj ├── EntityFramework.Metadata.sln ├── EntityFramework.Metadata.Net45.sln ├── .gitattributes └── .gitignore ├── README.md └── LICENSE /src/EntityFramework.Metadata/NuSpec/install.ps1: -------------------------------------------------------------------------------- 1 | $DTE.ItemOperations.Navigate("https://github.com/schneidsDotNet/EntityFramework.Metadata/blob/master/README.md#entityframeworkmetadata") 2 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 2 | { 3 | public abstract class Entity : EntityWithTypedId 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/EntityWithTypedId.cs: -------------------------------------------------------------------------------- 1 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 2 | { 3 | public abstract class EntityWithTypedId 4 | { 5 | public T Id { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/EntityWithMappedPk.cs: -------------------------------------------------------------------------------- 1 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 2 | { 3 | public class EntityWithMappedPk 4 | { 5 | public int BancoId { get; set; } 6 | public string Nombre { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Exceptions/ParentNotMappedYetException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace EntityFramework.Metadata.Exceptions 7 | { 8 | public class ParentNotMappedYetException : Exception 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/ComplexTypes/Contact.cs: -------------------------------------------------------------------------------- 1 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain.ComplexTypes 2 | { 3 | public class Contact 4 | { 5 | public string PhoneNumber { get; set; } 6 | public Address BusinessAddress { get; set; } 7 | public Address ShippingAddress { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/Foo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 7 | { 8 | public class Foo 9 | { 10 | public int Id { get; set; } 11 | public string Bar { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/MeteringPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 4 | { 5 | public class MeteringPoint : Entity 6 | { 7 | public string EIC { get; set; } 8 | 9 | public DateTime CreatedAt { get; set; } 10 | public DateTime? ModifiedAt { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/PageTranslations.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 3 | { 4 | public class PageTranslations 5 | { 6 | public int PageId { get; set; } 7 | 8 | public virtual Page Page { get; set; } 9 | 10 | public string Language { get; set; } 11 | 12 | public string Title { get; set; } 13 | 14 | public string Content { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Mappers/TphData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Data.Entity.Core.Metadata.Edm; 3 | 4 | namespace EntityFramework.Metadata.Mappers 5 | { 6 | internal class TphData 7 | { 8 | public EdmMember[] Properties { get; set; } 9 | public NavigationProperty[] NavProperties { get; set; } 10 | 11 | public Dictionary Discriminators = new Dictionary(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/Reading.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 4 | { 5 | public class Reading : Entity 6 | { 7 | public int MeteringPointId { get; set; } 8 | public virtual MeteringPoint MeteringPoint { get; set; } 9 | public DateTime Date { get; set; } 10 | public decimal? Consumed { get; set; } 11 | public decimal? Produced { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/TestModel.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/TestModel1.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/TestUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EntityFramework.Metadata.Test.CodeFirst.Domain.ComplexTypes; 3 | 4 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 5 | { 6 | public class TestUser : EntityWithTypedId 7 | { 8 | public Contact Contact { get; set; } 9 | public string FirstName { get; set; } 10 | public string LastName { get; set; } 11 | public string FullName { get { return string.Format("{0} {1}", FirstName, LastName); }} 12 | } 13 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/House.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using EntityFramework.Metadata.Test.CodeFirst.Domain.ComplexTypes; 7 | 8 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 9 | { 10 | public class House 11 | { 12 | public int Id { get; set; } 13 | 14 | public string Name { get; set; } 15 | 16 | // complex type must be the last member 17 | public Address Address { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/ComplexTypes/Address.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity.Spatial; 2 | 3 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain.ComplexTypes 4 | { 5 | public class Address 6 | { 7 | public string Country { get; set; } 8 | public string County { get; set; } 9 | public string City { get; set; } 10 | public string PostalCode { get; set; } 11 | public string StreetAddress { get; set; } 12 | 13 | #if !NET40 14 | public DbGeography Location { get; set; } 15 | public DbGeometry Shape { get; set; } 16 | #endif 17 | } 18 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/Company.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using EntityFramework.Metadata.Test.CodeFirst.Domain.ComplexTypes; 7 | 8 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 9 | { 10 | public class Company 11 | { 12 | public int Id { get; set; } 13 | public string Name { get; set; } 14 | public Contact FirstContact { get; set; } 15 | public Contact SecondContact { get; set; } 16 | public Address BusinessAddress { get; set; } 17 | public Address ShippingAddress { get; set; } 18 | public DateTime BusinessStartDate { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/IDbMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EntityFramework.Metadata 4 | { 5 | public interface IDbMapping 6 | { 7 | /// 8 | /// Tables in database 9 | /// 10 | ITableMapping[] Tables { get; } 11 | 12 | /// 13 | /// Get table mapping by entity type 14 | /// 15 | /// 16 | /// 17 | ITableMapping this[Type type] { get; } 18 | 19 | /// 20 | /// Get table mapping by entity type full name 21 | /// 22 | /// 23 | /// 24 | ITableMapping this[string typeFullName] { get; } 25 | } 26 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/TestModel.Designer.cs: -------------------------------------------------------------------------------- 1 | // T4 code generation is enabled for model 'C:\dev\EntityFramework.Metadata\trunk\src\EntityFramework.Metadata.Test\DbFirst\TestModel.edmx'. 2 | // To enable legacy code generation, change the value of the 'Code Generation Strategy' designer 3 | // property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model 4 | // is open in the designer. 5 | 6 | // If no context and entity classes have been generated, it may be because you created an empty model but 7 | // have not yet chosen which version of Entity Framework to use. To generate a context class and entity 8 | // classes for your model, open the model in the designer, right-click on the designer surface, and 9 | // select 'Update Model from Database...', 'Generate Database from Model...', or 'Add Code Generation 10 | // Item...'. -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/TestModel1.Designer.cs: -------------------------------------------------------------------------------- 1 | // T4 code generation is enabled for model 'D:\Development\EntityFramework.MappingApi\EntityFramework.Metadata.Test\DbFirst\TestModel.edmx'. 2 | // To enable legacy code generation, change the value of the 'Code Generation Strategy' designer 3 | // property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model 4 | // is open in the designer. 5 | 6 | // If no context and entity classes have been generated, it may be because you created an empty model but 7 | // have not yet chosen which version of Entity Framework to use. To generate a context class and entity 8 | // classes for your model, open the model in the designer, right-click on the designer surface, and 9 | // select 'Update Model from Database...', 'Generate Database from Model...', or 'Add Code Generation 10 | // Item...'. -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/Page.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 5 | { 6 | public class Page 7 | { 8 | public int PageId { get; set; } 9 | 10 | public string Content { get; set; } 11 | 12 | public string Title { get; set; } 13 | 14 | public int? ParentId { get; set; } 15 | 16 | public virtual Page Parent { get; set; } 17 | 18 | public virtual ICollection Translations { get; set; } 19 | 20 | public Guid CreatedById { get; set; } 21 | public virtual TestUser CreatedBy { get; set; } 22 | public DateTime CreatedAt { get; set; } 23 | 24 | public Guid? ModifiedById { get; set; } 25 | public virtual TestUser ModifiedBy { get; set; } 26 | public DateTime? ModifiedAt { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/Post.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace EntityFramework.Metadata.Test.DbFirst 11 | { 12 | using System; 13 | using System.Collections.Generic; 14 | 15 | public partial class Post 16 | { 17 | public int PostId { get; set; } 18 | public string Title { get; set; } 19 | public string Content { get; set; } 20 | public int BlogId { get; set; } 21 | 22 | public virtual Blogs Blogs { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/Blogs.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace EntityFramework.Metadata.Test.DbFirst 11 | { 12 | using System; 13 | using System.Collections.Generic; 14 | 15 | public partial class Blogs 16 | { 17 | public Blogs() 18 | { 19 | this.Posts = new HashSet(); 20 | } 21 | 22 | public int BlogId { get; set; } 23 | public string Name { get; set; } 24 | public string Url { get; set; } 25 | 26 | public virtual ICollection Posts { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/TestModel.edmx.diagram: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/TestModel.Context.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace EntityFramework.Metadata.Test.DbFirst 11 | { 12 | using System; 13 | using System.Data.Entity; 14 | using System.Data.Entity.Infrastructure; 15 | 16 | public partial class efmapping_testEntities : DbContext 17 | { 18 | public efmapping_testEntities() 19 | : base("name=efmapping_testEntities") 20 | { 21 | } 22 | 23 | protected override void OnModelCreating(DbModelBuilder modelBuilder) 24 | { 25 | throw new UnintentionalCodeFirstException(); 26 | } 27 | 28 | public virtual DbSet Blogs { get; set; } 29 | public virtual DbSet Posts { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/TestModel.Context1.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace EntityFramework.Metadata.Test.DbFirst 11 | { 12 | using System; 13 | using System.Data.Entity; 14 | using System.Data.Entity.Infrastructure; 15 | 16 | public partial class efmapping_testEntities : DbContext 17 | { 18 | public efmapping_testEntities() 19 | : base("name=efmapping_testEntities") 20 | { 21 | } 22 | 23 | protected override void OnModelCreating(DbModelBuilder modelBuilder) 24 | { 25 | throw new UnintentionalCodeFirstException(); 26 | } 27 | 28 | public virtual DbSet Blogs { get; set; } 29 | public virtual DbSet Posts { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EntityFramework.Metadata # 2 | [![Nuget](https://img.shields.io/nuget/v/EntityFramework.Metadata.svg)](https://www.nuget.org/packages/EntityFramework.Metadata/) 3 | 4 | Get table metadata for your Entity Framework entities. Need to know the table name of your entity programatically? Schema name? Column name from a property? EntityFramework.Metadata has that and more. 5 | 6 | static void Main() 7 | { 8 | var context = new MyDbContext(); 9 | var personData = context.Db(); 10 | 11 | Console.WriteLine(personData.TableName); 12 | // output: People 13 | 14 | var nameColumn = personData.Prop("Name"); 15 | 16 | Console.WriteLine(nameColumn.ColumnName); 17 | // output: MyName 18 | } 19 | 20 | [Table("People")] 21 | public class Person 22 | { 23 | public int PersonId { get; set; } 24 | [Column("MyName")] 25 | public string Name { get; set; } 26 | } 27 | 28 | Forked from [EntityFramework.MappingAPI](https://efmappingapi.codeplex.com/) by Markko Legonkov. Fixed bugs, added some unit tests, simplified the code and project structure, and removed support for EF5 and below. 29 | 30 | ### License ### 31 | This project is licensed under the MIT license. Portions of the code are subject to the BSD Simplified license - see LICENSE for more info. 32 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/EntityFramework.Metadata.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EntityFramework.Metadata 5 | $version$ 6 | EntityFramework.Metadata 7 | Spencer Schneidenbach 8 | Spencer Schneidenbach 9 | https://github.com/schneidsDotNet/EntityFramework.Metadata/blob/master/LICENSE 10 | https://github.com/schneidsDotNet/EntityFramework.Metadata 11 | false 12 | Table metadata provider for your Entity Framework entities. 13 | Table metadata provider for your Entity Framework entities. 14 | Copyright 2016 15 | EntityFramework 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/Employee.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 4 | { 5 | public abstract class EmployeeTPT 6 | { 7 | public int Id { get; set; } 8 | public string Name { get; set; } 9 | public string JobTitle { get; set; } 10 | } 11 | 12 | public class WorkerTPT : EmployeeTPT 13 | { 14 | public int? RefereeId { get; set; } 15 | public virtual WorkerTPT Referee { get; set; } 16 | public virtual ManagerTPT Boss { get; set; } 17 | } 18 | 19 | public class ManagerTPT : EmployeeTPT 20 | { 21 | public string Rank { get; set; } 22 | public virtual ICollection Henchmen { get; set; } 23 | } 24 | 25 | 26 | public abstract class EmployeeTPH 27 | { 28 | public string NameWithTitle { get { return string.Format("{0} ({1})", Name, Title); } } 29 | 30 | public int Id { get; set; } 31 | public string Name { get; set; } 32 | public string Title { get; set; } 33 | } 34 | 35 | public class AWorkerTPH : EmployeeTPH 36 | { 37 | public int BossId { get; set; } 38 | public int RefId { get; set; } 39 | public virtual ManagerTPH Boss { get; set; } 40 | } 41 | 42 | public class ManagerTPH : EmployeeTPH 43 | { 44 | public string Rank { get; set; } 45 | public int? RefId { get; set; } 46 | public virtual ICollection Henchmen { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("EntityFramework.Metadata")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("EntityFramework.Metadata")] 14 | [assembly: AssemblyCopyright("Copyright © 2016")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // Setting ComVisible to false makes the types in this assembly not visible 19 | // to COM components. If you need to access a type in this assembly from 20 | // COM, set the ComVisible attribute to true on that type. 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | [assembly: Guid("79ca2e96-9fed-4351-975c-6009c381c3a8")] 25 | 26 | // Version information for an assembly consists of the following four values: 27 | // 28 | // Major Version 29 | // Minor Version 30 | // Build Number 31 | // Revision 32 | // 33 | // You can specify all the values or you can default the Build and Revision Numbers 34 | // by using the '*' as shown below: 35 | // [assembly: AssemblyVersion("1.0.*")] 36 | 37 | [assembly: AssemblyVersion("1.0.1.0")] 38 | [assembly: AssemblyFileVersion("1.0.1.0")] 39 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EntityFramework.Metadata.Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EntityFramework.Metadata.Test")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e923c408-9b21-4ff4-8d71-14b8ad16f5a8")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/TestBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using NUnit.Framework; 6 | 7 | namespace EntityFramework.Metadata.Test.CodeFirst 8 | { 9 | [TestFixture] 10 | public abstract class TestBase 11 | { 12 | protected const int NvarcharMax = 1073741823; 13 | [SetUp] 14 | public virtual void Setup() 15 | { 16 | if (!Database.Exists("TestContext")) 17 | { 18 | Database.SetInitializer(new CreateDatabaseIfNotExists()); 19 | } 20 | else 21 | { 22 | Database.SetInitializer(null); 23 | } 24 | } 25 | 26 | protected TestContext GetContext() 27 | { 28 | var ctx = new TestContext(); 29 | 30 | ctx.Configuration.AutoDetectChangesEnabled = false; 31 | ctx.Configuration.LazyLoadingEnabled = false; 32 | ctx.Configuration.ProxyCreationEnabled = false; 33 | ctx.Configuration.ValidateOnSaveEnabled = false; 34 | 35 | return ctx; 36 | } 37 | 38 | protected void InitializeContext() 39 | { 40 | using (var ctx = GetContext()) 41 | { 42 | var sw = new Stopwatch(); 43 | sw.Start(); 44 | var tmp = ctx.Pages.Count(); 45 | sw.Stop(); 46 | Console.WriteLine("Initializing dbmodel took: {0}ms", sw.Elapsed.TotalMilliseconds); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFramework.Metadata", "EntityFramework.Metadata\EntityFramework.Metadata.csproj", "{609BE91A-F11B-49BC-98D9-F7C83517A59E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFramework.Metadata.Test", "EntityFramework.Metadata.Test\EntityFramework.Metadata.Test.csproj", "{C9530AA5-ACAD-4892-96E8-987A7847C341}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {609BE91A-F11B-49BC-98D9-F7C83517A59E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {609BE91A-F11B-49BC-98D9-F7C83517A59E}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {609BE91A-F11B-49BC-98D9-F7C83517A59E}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {609BE91A-F11B-49BC-98D9-F7C83517A59E}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {C9530AA5-ACAD-4892-96E8-987A7847C341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C9530AA5-ACAD-4892-96E8-987A7847C341}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C9530AA5-ACAD-4892-96E8-987A7847C341}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C9530AA5-ACAD-4892-96E8-987A7847C341}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Net45.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFramework.Metadata.Net45", "EntityFramework.Metadata\EntityFramework.Metadata.Net45.csproj", "{168DD262-C4DB-49E8-BF62-BFDD3B6DA41E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityFramework.Metadata.Test.Net45", "EntityFramework.Metadata.Test\EntityFramework.Metadata.Test.Net45.csproj", "{BC13B004-21FE-4A7A-8B8B-728E4A9CEC67}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {168DD262-C4DB-49E8-BF62-BFDD3B6DA41E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {168DD262-C4DB-49E8-BF62-BFDD3B6DA41E}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {168DD262-C4DB-49E8-BF62-BFDD3B6DA41E}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {168DD262-C4DB-49E8-BF62-BFDD3B6DA41E}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {BC13B004-21FE-4A7A-8B8B-728E4A9CEC67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {BC13B004-21FE-4A7A-8B8B-728E4A9CEC67}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {BC13B004-21FE-4A7A-8B8B-728E4A9CEC67}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {BC13B004-21FE-4A7A-8B8B-728E4A9CEC67}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Extensions/MappingApiExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity; 3 | using System.Linq.Expressions; 4 | 5 | namespace EntityFramework.Metadata.Extensions 6 | { 7 | public static class MappingApiExtensions 8 | { 9 | /// 10 | /// 11 | /// 12 | /// 13 | /// 14 | public static IEntityMap[] Db(this DbContext ctx) 15 | { 16 | return EfMap.Get(ctx).Tables; 17 | } 18 | 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | public static IEntityMap Db(this DbContext ctx) 26 | { 27 | return EfMap.Get(ctx); 28 | } 29 | 30 | /// 31 | /// 32 | /// 33 | /// 34 | /// 35 | /// 36 | /// 37 | /// 38 | public static IEntityMap Db(this T ctx, Expression>> dbset) where T : DbContext where T1 : class 39 | { 40 | return ctx.Db(); 41 | } 42 | 43 | /// 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | public static IEntityMap Db(this DbContext ctx, Type type) 50 | { 51 | return EfMap.Get(ctx)[type]; 52 | } 53 | 54 | /// 55 | /// 56 | /// 57 | /// 58 | /// 59 | /// 60 | public static IEntityMap Db(this DbContext ctx, string typeFullName) 61 | { 62 | return EfMap.Get(ctx)[typeFullName]; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Mappers/CodeFirstMapper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using EntityFramework.Metadata.Exceptions; 4 | using System.Data.Entity.Core.Metadata.Edm; 5 | 6 | namespace EntityFramework.Metadata.Mappers 7 | { 8 | internal class CodeFirstMapper : MapperBase 9 | { 10 | public CodeFirstMapper(MetadataWorkspace metadataWorkspace, EntityContainer entityContainer) 11 | : base(metadataWorkspace, entityContainer) 12 | { 13 | } 14 | 15 | protected string GetTableName(EntitySet entitySet) 16 | { 17 | return (string)entitySet.MetadataProperties["Table"].Value; 18 | } 19 | 20 | protected override PrepareMappingRes PrepareMapping(string typeFullName, EdmType edmItem) 21 | { 22 | // find existing parent storageEntitySet 23 | // thp derived types does not have storageEntitySet 24 | EntitySet storageEntitySet; 25 | EdmType baseEdmType = edmItem; 26 | while (!EntityContainer.TryGetEntitySetByName(baseEdmType.Name, false, out storageEntitySet)) 27 | { 28 | if (baseEdmType.BaseType == null) 29 | { 30 | break; 31 | } 32 | baseEdmType = baseEdmType.BaseType; 33 | } 34 | 35 | if (storageEntitySet == null) 36 | { 37 | return null; 38 | } 39 | 40 | var isRoot = baseEdmType == edmItem; 41 | if (!isRoot) 42 | { 43 | var parent = _entityMaps.Values.FirstOrDefault(x => x.EdmType == baseEdmType); 44 | // parent table has not been mapped yet 45 | if (parent == null) 46 | { 47 | throw new ParentNotMappedYetException(); 48 | } 49 | } 50 | 51 | string tableName = GetTableName(storageEntitySet); 52 | 53 | return new PrepareMappingRes { TableName = tableName, StorageEntitySet = storageEntitySet, IsRoot = isRoot, BaseEdmType = baseEdmType }; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/EfMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using EntityFramework.Metadata.Mappings; 5 | using System.Data.Entity.Infrastructure; 6 | 7 | namespace EntityFramework.Metadata 8 | { 9 | /// 10 | /// 11 | /// 12 | internal class EfMap 13 | { 14 | /// 15 | /// 16 | /// 17 | private static readonly Dictionary Mappings = new Dictionary(); 18 | 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// 24 | /// 25 | public static IEntityMap Get(DbContext context) 26 | { 27 | return (IEntityMap)Get(context)[typeof(T)]; 28 | } 29 | 30 | /// 31 | /// 32 | /// 33 | /// 34 | public static IEntityMap Get(DbContext context, Type type) 35 | { 36 | return Get(context)[type]; 37 | } 38 | 39 | /// 40 | /// 41 | /// 42 | /// 43 | public static IEntityMap Get(DbContext context, string typeFullName) 44 | { 45 | return Get(context)[typeFullName]; 46 | } 47 | 48 | /// 49 | /// 50 | /// 51 | /// 52 | /// 53 | public static DbMapping Get(DbContext context) 54 | { 55 | var cackeKey = context.GetType().FullName; 56 | 57 | var iDbModelCacheKeyProvider = context as IDbModelCacheKeyProvider; 58 | if (iDbModelCacheKeyProvider != null) 59 | { 60 | cackeKey = iDbModelCacheKeyProvider.CacheKey; 61 | } 62 | 63 | if (Mappings.ContainsKey(cackeKey)) 64 | { 65 | return Mappings[cackeKey]; 66 | } 67 | 68 | var mapping = new DbMapping(context); 69 | //var mapping = Map(context); 70 | 71 | Mappings[cackeKey] = mapping; 72 | return mapping; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/IEntityMap.cs: -------------------------------------------------------------------------------- 1 | 2 | using System; 3 | using System.Linq.Expressions; 4 | using System.Security.Cryptography.X509Certificates; 5 | 6 | namespace EntityFramework.Metadata 7 | { 8 | /// 9 | /// Generic entity map 10 | /// 11 | /// Entity type 12 | public interface IEntityMap : IEntityMap 13 | { 14 | /// 15 | /// Get property mapping by predicate 16 | /// 17 | /// 18 | /// 19 | /// 20 | IPropertyMap Prop(Expression> predicate); 21 | } 22 | 23 | public interface IEntityMap 24 | { 25 | /// 26 | /// Entity type 27 | /// 28 | Type Type { get; } 29 | 30 | /// 31 | /// Table name in database 32 | /// 33 | string TableName { get; } 34 | 35 | /// 36 | /// Table schema 37 | /// 38 | string Schema { get; } 39 | 40 | /// 41 | /// Is table-per-hierarchy mapping 42 | /// 43 | bool IsTph { get; } 44 | 45 | /// 46 | /// Is table-per-hierarchy base entity 47 | /// 48 | bool IsRoot { get; } 49 | 50 | /// 51 | /// Mapped properties 52 | /// 53 | IPropertyMap[] Properties { get; } 54 | 55 | /// 56 | /// Foreign key properties 57 | /// 58 | IPropertyMap[] Fks { get; } 59 | 60 | /// 61 | /// Primary key properties 62 | /// 63 | IPropertyMap[] Pks { get; } 64 | 65 | /// 66 | /// Tph entity discriminators 67 | /// 68 | IPropertyMap[] Discriminators { get; } 69 | 70 | /// 71 | /// Gets property map by property name 72 | /// 73 | /// 74 | /// 75 | IPropertyMap this[string property] { get; } 76 | 77 | /// 78 | /// Gets property map by property name 79 | /// 80 | /// 81 | /// 82 | IPropertyMap Prop(string propertyName); 83 | } 84 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Spencer Schneidenbach 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | 25 | 26 | Code initially forked from http://efmappingapi.codeplex.com is licensed under BSD Simplified 27 | 28 | Copyright (c) 2014, Markko Legonkov 29 | All rights reserved. 30 | 31 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 32 | 33 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 34 | 35 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 36 | 37 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | 39 | -------------------------------------------------------------------------------- /src/.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/Domain/Contract.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EntityFramework.Metadata.Test.CodeFirst.Domain 4 | { 5 | public abstract class ContractBase : Entity 6 | { 7 | public string ContractNr { get; set; } 8 | public string AvpContractNr { get; set; } 9 | 10 | public int MeteringPointId { get; set; } 11 | public virtual MeteringPoint MeteringPoint { get; set; } 12 | 13 | public DateTime StartDate { get; set; } 14 | 15 | public DateTime? EndDate { get; set; } 16 | 17 | public DateTime? ContractSignedAt { get; set; } 18 | 19 | /// 20 | /// Utc time of contract start date. Readonly 21 | /// 22 | public DateTime StartDateUtc { get; protected set; } 23 | 24 | /// 25 | /// Utc time of contract end date. Readonly 26 | /// 27 | public DateTime? EndDateUtc { get; protected set; } 28 | 29 | public int ClientId { get; set; } 30 | 31 | public decimal? FixedPrice { get; set; } 32 | 33 | public int? PackageId { get; set; } 34 | public string PackageName { get; set; } 35 | 36 | public bool Validated { get; set; } 37 | 38 | public DateTime CreatedAt { get; set; } 39 | public DateTime? ModifiedAt { get; set; } 40 | public DateTime? LastPricesCalculatedAt { get; set; } 41 | 42 | protected ContractBase() 43 | { 44 | StartDate = DateTime.Now; 45 | StartDateUtc = DateTime.Now; 46 | CreatedAt = DateTime.Now; 47 | } 48 | } 49 | 50 | public class Contract : ContractBase 51 | { 52 | 53 | } 54 | 55 | public class ContractFixed : ContractBase 56 | { 57 | public int PackageFixedId { get; set; } 58 | 59 | public string PricesJson { get; set; } 60 | } 61 | 62 | public class ContractStock : ContractBase 63 | { 64 | public decimal? Margin { get; set; } 65 | 66 | public int PackageStockId { get; set; } 67 | } 68 | 69 | public class ContractKomb1 : ContractBase 70 | { 71 | public int PackageKomb1Id { get; set; } 72 | 73 | public decimal Base { get; set; } 74 | public decimal? StockMargin { get; set; } 75 | public string FixPricesJson { get; set; } 76 | 77 | public int SubPackageId { get; set; } 78 | } 79 | 80 | public class ContractKomb2 : ContractBase 81 | { 82 | public int PackageKomb2Id { get; set; } 83 | 84 | public decimal? Part1Margin { get; set; } 85 | public string Part1PricesJson { get; set; } 86 | 87 | public int Part1SubPackageId { get; set; } 88 | 89 | public decimal? Part2Margin { get; set; } 90 | public string Part2PricesJson { get; set; } 91 | 92 | public int Part2SubPackageId { get; set; } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Mappers/DbFirstMapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using EntityFramework.Metadata.Exceptions; 5 | using EntityFramework.Metadata.Extensions; 6 | using System.Data.Entity.Core.Mapping; 7 | using System.Data.Entity.Core.Metadata.Edm; 8 | 9 | 10 | namespace EntityFramework.Metadata.Mappers 11 | { 12 | internal class DbFirstMapper : MapperBase 13 | { 14 | public DbFirstMapper(MetadataWorkspace metadataWorkspace, EntityContainer entityContainer) 15 | : base(metadataWorkspace, entityContainer) 16 | { 17 | } 18 | 19 | protected override PrepareMappingRes PrepareMapping(string typeFullName, EdmType edmItem) 20 | { 21 | string tableName = GetTableName(typeFullName); 22 | 23 | // find existing parent storageEntitySet 24 | // thp derived types does not have storageEntitySet 25 | EntitySet storageEntitySet; 26 | EdmType baseEdmType = edmItem; 27 | while (!EntityContainer.TryGetEntitySetByName(tableName, false, out storageEntitySet)) 28 | { 29 | if (baseEdmType.BaseType == null) 30 | { 31 | break; 32 | } 33 | baseEdmType = baseEdmType.BaseType; 34 | } 35 | 36 | if (storageEntitySet == null) 37 | { 38 | return null; 39 | } 40 | 41 | var isRoot = baseEdmType == edmItem; 42 | if (!isRoot) 43 | { 44 | var parent = _entityMaps.Values.FirstOrDefault(x => x.EdmType == baseEdmType); 45 | // parent table has not been mapped yet 46 | if (parent == null) 47 | { 48 | throw new ParentNotMappedYetException(); 49 | } 50 | } 51 | 52 | return new PrepareMappingRes { TableName = tableName, StorageEntitySet = storageEntitySet, IsRoot = isRoot, BaseEdmType = baseEdmType }; 53 | } 54 | 55 | protected string GetTableName(string typeFullName) 56 | { 57 | // Get the entity type from the model that maps to the CLR type 58 | var entityType = MetadataWorkspace.GetItems(DataSpace.OSpace).Single(e => e.FullName == typeFullName); 59 | 60 | // Get the entity set that uses this entity type 61 | var entitySet = MetadataWorkspace 62 | .GetItems(DataSpace.CSpace) 63 | .Single() 64 | .EntitySets 65 | .Single(s => s.ElementType.Name == entityType.Name); 66 | 67 | var entitySetMappings = (IEnumerable)MetadataWorkspace.GetItems(DataSpace.CSSpace) 68 | .First() 69 | .GetPrivateFieldValue("EntitySetMappings"); 70 | 71 | object mapping = entitySetMappings 72 | .FirstOrDefault(map => map.GetPrivateFieldValue("EntitySet") == entitySet); 73 | 74 | 75 | object entityTypeMapping = ((IEnumerable)mapping.GetPrivateFieldValue("EntityTypeMappings")).FirstOrDefault(); 76 | var tables = (IEnumerable)entityTypeMapping.GetPrivateFieldValue("MappingFragments"); 77 | 78 | return (string)tables.FirstOrDefault().GetPrivateFieldValue("Table").GetPrivateFieldValue("Name"); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | QbTestProject/.svn/ 2 | ## Ignore Visual Studio temporary files, build results, and 3 | ## files generated by popular Visual Studio add-ons. 4 | 5 | # User-specific files 6 | *.suo 7 | *.user 8 | *.sln.docstates 9 | 10 | # Build results 11 | [Dd]ebug/ 12 | [Dd]ebugPublic/ 13 | [Rr]elease/ 14 | x64/ 15 | build/ 16 | bld/ 17 | [Bb]in/ 18 | [Oo]bj/ 19 | 20 | # Roslyn cache directories 21 | *.ide/ 22 | 23 | # MSTest test Results 24 | [Tt]est[Rr]esult*/ 25 | [Bb]uild[Ll]og.* 26 | 27 | #NUNIT 28 | *.VisualState.xml 29 | TestResult.xml 30 | 31 | # Build Results of an ATL Project 32 | [Dd]ebugPS/ 33 | [Rr]eleasePS/ 34 | dlldata.c 35 | 36 | *_i.c 37 | *_p.c 38 | *_i.h 39 | *.ilk 40 | *.meta 41 | *.obj 42 | *.pch 43 | *.pdb 44 | *.pgc 45 | *.pgd 46 | *.rsp 47 | *.sbr 48 | *.tlb 49 | *.tli 50 | *.tlh 51 | *.tmp 52 | *.tmp_proj 53 | *.log 54 | *.vspscc 55 | *.vssscc 56 | .builds 57 | *.pidb 58 | *.svclog 59 | *.scc 60 | 61 | # Chutzpah Test files 62 | _Chutzpah* 63 | 64 | # Visual C++ cache files 65 | ipch/ 66 | *.aps 67 | *.ncb 68 | *.opensdf 69 | *.sdf 70 | *.cachefile 71 | 72 | # Visual Studio profiler 73 | *.psess 74 | *.vsp 75 | *.vspx 76 | 77 | # TFS 2012 Local Workspace 78 | $tf/ 79 | 80 | # Guidance Automation Toolkit 81 | *.gpState 82 | 83 | # ReSharper is a .NET coding add-in 84 | _ReSharper*/ 85 | *.[Rr]e[Ss]harper 86 | *.DotSettings.user 87 | 88 | # JustCode is a .NET coding addin-in 89 | .JustCode 90 | 91 | # TeamCity is a build add-in 92 | _TeamCity* 93 | 94 | # DotCover is a Code Coverage Tool 95 | *.dotCover 96 | 97 | # NCrunch 98 | _NCrunch_* 99 | .*crunch*.local.xml 100 | 101 | # MightyMoose 102 | *.mm.* 103 | AutoTest.Net/ 104 | 105 | # Web workbench (sass) 106 | .sass-cache/ 107 | 108 | # Installshield output folder 109 | [Ee]xpress/ 110 | 111 | # DocProject is a documentation generator add-in 112 | DocProject/buildhelp/ 113 | DocProject/Help/*.HxT 114 | DocProject/Help/*.HxC 115 | DocProject/Help/*.hhc 116 | DocProject/Help/*.hhk 117 | DocProject/Help/*.hhp 118 | DocProject/Help/Html2 119 | DocProject/Help/html 120 | 121 | # Click-Once directory 122 | publish/ 123 | 124 | # Publish Web Output 125 | *.[Pp]ublish.xml 126 | *.azurePubxml 127 | ## TODO: Comment the next line if you want to checkin your 128 | ## web deploy settings but do note that will include unencrypted 129 | ## passwords 130 | *.pubxml 131 | 132 | # NuGet Packages Directory 133 | [Pp]ackages/* 134 | ## TODO: If the tool you use requires repositories.config 135 | ## uncomment the next line 136 | #!packages/repositories.config 137 | 138 | # Enable "build/" folder in the NuGet Packages folder since 139 | # NuGet packages use it for MSBuild targets. 140 | # This line needs to be after the ignore of the build folder 141 | # (and the packages folder if the line above has been uncommented) 142 | ![Pp]ackages/build/ 143 | 144 | # Windows Azure Build Output 145 | csx/ 146 | *.build.csdef 147 | 148 | # Windows Store app package directory 149 | AppPackages/ 150 | 151 | # Others 152 | sql/ 153 | *.Cache 154 | ClientBin/ 155 | [Ss]tyle[Cc]op.* 156 | ~$* 157 | *~ 158 | *.dbmdl 159 | *.dbproj.schemaview 160 | *.pfx 161 | *.publishsettings 162 | node_modules/ 163 | 164 | # RIA/Silverlight projects 165 | Generated_Code/ 166 | 167 | # Backup & report files from converting an old project file 168 | # to a newer Visual Studio version. Backup files are not needed, 169 | # because we have git ;-) 170 | _UpgradeReport_Files/ 171 | Backup*/ 172 | UpgradeLog*.XML 173 | UpgradeLog*.htm 174 | 175 | # SQL Server files 176 | *.mdf 177 | *.ldf 178 | 179 | # Business Intelligence projects 180 | *.rdl.data 181 | *.bim.layout 182 | *.bim_*.settings 183 | 184 | # Microsoft Fakes 185 | FakesAssemblies/ -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/IPropertyMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EntityFramework.Metadata.Mappings; 3 | 4 | namespace EntityFramework.Metadata 5 | { 6 | public interface IPropertyMap 7 | { 8 | /// 9 | /// Table column name property is mapped to 10 | /// 11 | string ColumnName { get; } 12 | 13 | /// 14 | /// Entity property name 15 | /// 16 | string PropertyName { get; } 17 | 18 | /// 19 | /// Is column primary key 20 | /// 21 | bool IsPk { get; } 22 | 23 | /// 24 | /// Is column nullable 25 | /// 26 | bool IsRequired { get; } 27 | 28 | /// 29 | /// Column default value 30 | /// 31 | object DefaultValue { get; } 32 | 33 | /// 34 | /// 35 | /// 36 | bool IsIdentity { get; } 37 | 38 | /// 39 | /// 40 | /// 41 | bool Computed { get; } 42 | 43 | /// 44 | /// Column max length 45 | /// 46 | int MaxLength { get; } 47 | 48 | /// 49 | /// Data type stored in the column 50 | /// 51 | Type Type { get; } 52 | 53 | /// 54 | /// Is table-per-hierarchy discriminator 55 | /// 56 | bool IsDiscriminator { get; } 57 | 58 | /// 59 | /// 60 | /// 61 | byte Precision { get; } 62 | 63 | /// 64 | /// 65 | /// 66 | byte Scale { get; } 67 | 68 | /// 69 | /// 70 | /// 71 | bool Unicode { get; } 72 | 73 | /// 74 | /// 75 | /// 76 | bool FixedLength { get; } 77 | 78 | /// 79 | /// Paren entity mapping 80 | /// 81 | IEntityMap EntityMap { get; } 82 | 83 | /// 84 | /// 85 | /// 86 | Delegate Selector { get; } 87 | 88 | #region nav property 89 | 90 | /// 91 | /// Is navigation property 92 | /// 93 | bool IsNavigationProperty { get; } 94 | 95 | /// 96 | /// Foreign key property name which is used for navigation property. 97 | /// 98 | string ForeignKeyPropertyName { get; } 99 | 100 | /// 101 | /// Foreign key property. 102 | /// Available only for navigation properties. 103 | /// 104 | IPropertyMap ForeignKey { get; } 105 | 106 | #endregion 107 | 108 | #region fk property 109 | 110 | /// 111 | /// Is foreign key 112 | /// 113 | bool IsFk { get; } 114 | 115 | /// 116 | /// Foreign keys navigation propery name 117 | /// 118 | string NavigationPropertyName { get; } 119 | 120 | /// 121 | /// Foreign key target column 122 | /// 123 | IPropertyMap FkTargetColumn { get; } 124 | 125 | /// 126 | /// Foreign key navigation property 127 | /// 128 | IPropertyMap NavigationProperty { get; } 129 | 130 | /// 131 | /// 132 | /// 133 | int? SRID { get; } 134 | 135 | /// 136 | /// 137 | /// 138 | bool IsStrict { get; } 139 | 140 | #endregion 141 | } 142 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/EntityFramework.Metadata.Net45.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {168DD262-C4DB-49E8-BF62-BFDD3B6DA41E} 8 | Library 9 | Properties 10 | EntityFramework.Metadata 11 | EntityFramework.Metadata 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | true 26 | full 27 | false 28 | bin\Debug\NET45\ 29 | TRACE;DEBUG;NET45 30 | prompt 31 | 4 32 | false 33 | 34 | 35 | pdbonly 36 | true 37 | bin\Release\NET45\ 38 | TRACE;NET45 39 | prompt 40 | 4 41 | false 42 | bin\Release\NET45\EntityFramework.Metadata.xml 43 | 44 | 45 | 46 | False 47 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 48 | 49 | 50 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/EntityFramework.Metadata.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {609BE91A-F11B-49BC-98D9-F7C83517A59E} 8 | Library 9 | Properties 10 | EntityFramework.Metadata 11 | EntityFramework.Metadata 12 | v4.0 13 | 512 14 | 15 | SAK 16 | SAK 17 | SAK 18 | SAK 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\NET40\ 25 | TRACE;DEBUG;NET40 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\NET40\ 33 | TRACE;NET40 34 | prompt 35 | 4 36 | bin\Release\NET40\EntityFramework.Metadata.xml 37 | 38 | 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | False 48 | ..\packages\EntityFramework.6.1.3\lib\net40\EntityFramework.dll 49 | 50 | 51 | ..\packages\EntityFramework.6.1.3\lib\net40\EntityFramework.SqlServer.dll 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 92 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Mappings/DbMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Data.Entity; 5 | using System.Data.Entity.Infrastructure; 6 | //using System.Data.Entity.SqlServer; 7 | using System.Linq; 8 | using EntityFramework.Metadata.Exceptions; 9 | using EntityFramework.Metadata.Extensions; 10 | using EntityFramework.Metadata.Mappers; 11 | using System.Data.Entity.Core.Metadata.Edm; 12 | 13 | namespace EntityFramework.Metadata.Mappings 14 | { 15 | /// 16 | /// 17 | /// 18 | internal class DbMapping 19 | { 20 | private readonly Dictionary _tableMappings = new Dictionary(); 21 | private readonly string _contextTypeName; 22 | private readonly DbContext _context; 23 | 24 | /// 25 | /// 26 | /// 27 | /// 28 | public DbMapping(DbContext context) 29 | { 30 | _context = context; 31 | _contextTypeName = context.GetType().FullName; 32 | 33 | var objectContext = ((IObjectContextAdapter)context).ObjectContext; 34 | MetadataWorkspace metadataWorkspace = objectContext.MetadataWorkspace; 35 | 36 | MapperBase mapper; 37 | 38 | EntityContainer entityContainer; 39 | if (metadataWorkspace.TryGetEntityContainer("CodeFirstDatabase", true, DataSpace.SSpace, out entityContainer)) 40 | { 41 | mapper = new CodeFirstMapper(metadataWorkspace, entityContainer); 42 | } 43 | else 44 | { 45 | ReadOnlyCollection readOnlyCollection; 46 | readOnlyCollection = metadataWorkspace.GetItems(DataSpace.SSpace); 47 | entityContainer = readOnlyCollection[0]; 48 | mapper = new DbFirstMapper(metadataWorkspace, entityContainer); 49 | } 50 | 51 | var typeMappings = mapper.TypeMappings; 52 | 53 | int depth = 0; 54 | while (true) 55 | { 56 | if (depth > 100) 57 | { 58 | throw new Exception("Type mapping has reached unreasonable depth."); 59 | } 60 | 61 | if (typeMappings.Count == 0) 62 | { 63 | break; 64 | } 65 | 66 | var nextLevel = new Dictionary(); 67 | 68 | foreach (var kvp in typeMappings) 69 | { 70 | EntityMap entityMap; 71 | try 72 | { 73 | entityMap = mapper.MapEntity(kvp.Key, kvp.Value); 74 | } 75 | catch (ParentNotMappedYetException) 76 | { 77 | nextLevel.Add(kvp.Key, kvp.Value); 78 | continue; 79 | } 80 | 81 | if (entityMap == null) 82 | { 83 | continue; 84 | } 85 | 86 | //tableMapping.DbMapping = this; 87 | _tableMappings.Add(kvp.Key, entityMap); 88 | } 89 | 90 | typeMappings = nextLevel; 91 | depth++; 92 | } 93 | 94 | mapper.BindForeignKeys(); 95 | } 96 | 97 | /// 98 | /// Tables in database 99 | /// 100 | public IEntityMap[] Tables { get { return _tableMappings.Values.ToArray(); } } 101 | 102 | /// 103 | /// Get table mapping by entity type 104 | /// 105 | /// 106 | /// 107 | public IEntityMap this[Type type] 108 | { 109 | get { return this[type.FullName]; } 110 | } 111 | 112 | /// 113 | /// Get table mapping by entity type full name 114 | /// 115 | /// 116 | /// 117 | public IEntityMap this[string typeFullName] 118 | { 119 | get 120 | { 121 | if (!_tableMappings.ContainsKey(typeFullName)) 122 | throw new Exception("Type '" + typeFullName + "' is not found in context '" + _contextTypeName + "'"); 123 | 124 | return _tableMappings[typeFullName]; 125 | } 126 | } 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/MappingTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using EntityFramework.Metadata.Extensions; 4 | using EntityFramework.Metadata.Test.CodeFirst; 5 | using NUnit.Framework; 6 | 7 | namespace EntityFramework.Metadata.Test.DbFirst 8 | { 9 | [TestFixture] 10 | public class MappingTest 11 | { 12 | protected const int NvarcharMax = 1073741823; 13 | 14 | public efmapping_testEntities GetContext() 15 | { 16 | return new efmapping_testEntities(); 17 | } 18 | 19 | [Test] 20 | public void TableNames() 21 | { 22 | using (var ctx = GetContext()) 23 | { 24 | var sw = new Stopwatch(); 25 | sw.Restart(); 26 | var dbmapping = ctx.Db(); 27 | sw.Start(); 28 | 29 | Console.WriteLine("Mapping took: {0}ms", sw.Elapsed.TotalMilliseconds); 30 | 31 | foreach (var tableMapping in dbmapping) 32 | { 33 | Console.WriteLine("{0}: {1}.{2}", tableMapping.Type.FullName, tableMapping.Schema, tableMapping.TableName); 34 | } 35 | 36 | Assert.AreEqual(ctx.Db().TableName, "Blogs"); 37 | Assert.AreEqual(ctx.Db().Schema, "dbo"); 38 | 39 | Assert.AreEqual(ctx.Db().TableName, "Posts"); 40 | Assert.AreEqual(ctx.Db().Schema, "dbo"); 41 | } 42 | } 43 | 44 | 45 | [Test] 46 | public void Entity_Blogs() 47 | { 48 | using (var ctx = GetContext()) 49 | { 50 | var map = ctx.Db(); 51 | Console.WriteLine("{0}:{1}", map.Type, map.TableName); 52 | 53 | map.Prop(x => x.BlogId) 54 | .HasColumnName("BlogId") 55 | .IsPk() 56 | .IsFk(false) 57 | .IsIdentity() 58 | .IsNavigationProperty(false); 59 | 60 | map.Prop(x => x.Name) 61 | .HasColumnName("Name") 62 | .IsPk(false) 63 | .IsFk(false) 64 | .IsIdentity(false) 65 | .IsRequired(false) 66 | .IsNavigationProperty(false) 67 | .MaxLength(200) 68 | .FixedLength(false) 69 | .Unicode(); 70 | 71 | map.Prop(x => x.Url) 72 | .HasColumnName("Url") 73 | .IsPk(false) 74 | .IsFk(false) 75 | .IsIdentity(false) 76 | .IsRequired(false) 77 | .IsNavigationProperty(false) 78 | .MaxLength(200) 79 | .FixedLength(false) 80 | .Unicode(); 81 | 82 | } 83 | } 84 | 85 | 86 | [Test] 87 | public void Entity_Posts() 88 | { 89 | using (var ctx = GetContext()) 90 | { 91 | var map = ctx.Db(); 92 | Console.WriteLine("{0}:{1}", map.Type, map.TableName); 93 | 94 | map.Prop(x => x.PostId) 95 | .HasColumnName("PostId") 96 | .IsPk() 97 | .IsFk(false) 98 | .IsIdentity() 99 | .IsNavigationProperty(false); 100 | 101 | map.Prop(x => x.Title) 102 | .HasColumnName("Title") 103 | .IsPk(false) 104 | .IsFk(false) 105 | .IsIdentity(false) 106 | .IsRequired(false) 107 | .IsNavigationProperty(false) 108 | .MaxLength(200) 109 | .FixedLength(false) 110 | .Unicode(); 111 | 112 | map.Prop(x => x.Content) 113 | .HasColumnName("Content") 114 | .IsPk(false) 115 | .IsFk(false) 116 | .IsIdentity(false) 117 | .IsRequired(false) 118 | .IsNavigationProperty(false) 119 | .MaxLength(NvarcharMax) 120 | .FixedLength(false) 121 | .Unicode(); 122 | 123 | map.Prop(x => x.BlogId) 124 | .IsFk() 125 | .IsRequired() 126 | .NavigationPropertyName("Blogs") 127 | .NavigationProperty(map.Prop(x => x.Blogs)); 128 | 129 | map.Prop(x => x.Blogs) 130 | .IsFk(false) 131 | .ForeignKey(map.Prop(x => x.BlogId)) 132 | .ForeignKeyPropertyName("BlogId"); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Mappings/PropertyMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity.Core.Metadata.Edm; 3 | 4 | namespace EntityFramework.Metadata.Mappings 5 | { 6 | /// 7 | /// 8 | /// 9 | internal class PropertyMap : IPropertyMap 10 | { 11 | /// 12 | /// Table column name 13 | /// 14 | public string ColumnName { get; private set; } 15 | 16 | /// 17 | /// Entity property name 18 | /// 19 | public string PropertyName { get; private set; } 20 | 21 | /// 22 | /// Is column primary key 23 | /// 24 | public bool IsPk { get; internal set; } 25 | 26 | /// 27 | /// Is column nullable 28 | /// 29 | public bool IsRequired { get; internal set; } 30 | 31 | /// 32 | /// Column default value 33 | /// 34 | public object DefaultValue { get; internal set; } 35 | 36 | /// 37 | /// 38 | /// 39 | public bool IsIdentity { get; internal set; } 40 | 41 | /// 42 | /// 43 | /// 44 | public bool Computed { get; internal set; } 45 | 46 | /// 47 | /// Column max length 48 | /// 49 | public int MaxLength { get; internal set; } 50 | 51 | /// 52 | /// Data type stored in the column 53 | /// 54 | public Type Type { get; internal set; } 55 | 56 | /// 57 | /// Is table-per-hierarchy discriminator 58 | /// 59 | public bool IsDiscriminator { get; internal set; } 60 | 61 | /// 62 | /// Paren table mapping 63 | /// 64 | public IEntityMap EntityMap { get; internal set; } 65 | 66 | /// 67 | /// 68 | /// 69 | public bool IsNavigationProperty { get; internal set; } 70 | 71 | /// 72 | /// Is foreign key 73 | /// 74 | public bool IsFk { get; internal set; } 75 | 76 | /// 77 | /// Foreign keys navigation propery name 78 | /// 79 | public string NavigationPropertyName { get; internal set; } 80 | 81 | /// 82 | /// 83 | /// 84 | public string ForeignKeyPropertyName { get; internal set; } 85 | 86 | /// 87 | /// 88 | /// 89 | public IPropertyMap ForeignKey { get; internal set; } 90 | 91 | /// 92 | /// Foreign key target column 93 | /// 94 | public IPropertyMap FkTargetColumn { get; internal set; } 95 | 96 | /// 97 | /// Foreign key navigation property 98 | /// 99 | public IPropertyMap NavigationProperty { get; internal set; } 100 | 101 | /// 102 | /// 103 | /// 104 | public bool Unicode { get; internal set; } 105 | 106 | /// 107 | /// 108 | /// 109 | public bool FixedLength { get; internal set; } 110 | 111 | /// 112 | /// 113 | /// 114 | public byte Precision { get; internal set; } 115 | 116 | /// 117 | /// 118 | /// 119 | public byte Scale { get; internal set; } 120 | 121 | /// 122 | /// 123 | /// 124 | public int? SRID { get; internal set; } 125 | 126 | /// 127 | /// 128 | /// 129 | public bool IsStrict { get; internal set; } 130 | 131 | /// 132 | /// 133 | /// 134 | public Delegate Selector { get; internal set; } 135 | 136 | /// 137 | /// Edm property from storage entity set (SSpace). 138 | /// This propery is needed to know which properties are already mapped to TPH entity. 139 | /// 140 | internal EdmProperty EdmProperty { get; set; } 141 | 142 | /// 143 | /// Edm member from CSpace. 144 | /// Stored for linking foreign keys. 145 | /// 146 | internal EdmMember EdmMember { get; set; } 147 | 148 | /// 149 | /// Edm member from CSpace. 150 | /// Stored for linking foreign keys. 151 | /// 152 | internal EdmMember FkTargetEdmMember { get; set; } 153 | 154 | /// 155 | /// 156 | /// 157 | /// 158 | /// 159 | internal PropertyMap(string property, string columnName) 160 | { 161 | ColumnName = columnName; 162 | PropertyName = property; 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Extensions/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | 5 | namespace EntityFramework.Metadata.Extensions 6 | { 7 | internal static class TypeExtensions 8 | { 9 | /// 10 | /// Returns a private Property Value from a given Object. Uses Reflection. 11 | /// Throws a ArgumentOutOfRangeException if the Property is not found. 12 | /// 13 | /// Object from where the Property Value is returned 14 | /// Propertyname as string. 15 | /// PropertyValue 16 | public static object GetPrivateFieldValue(this object obj, string propName) 17 | { 18 | if (obj == null) throw new ArgumentNullException("obj"); 19 | Type t = obj.GetType(); 20 | FieldInfo fieldInfo = null; 21 | PropertyInfo propertyInfo = null; 22 | while (fieldInfo == null && propertyInfo == null && t != null) 23 | { 24 | fieldInfo = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 25 | if (fieldInfo == null) 26 | { 27 | propertyInfo = t.GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 28 | } 29 | 30 | t = t.BaseType; 31 | } 32 | if (fieldInfo == null && propertyInfo == null) 33 | throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName)); 34 | 35 | if (fieldInfo != null) 36 | return fieldInfo.GetValue(obj); 37 | 38 | return propertyInfo.GetValue(obj, null); 39 | } 40 | 41 | /// 42 | /// 43 | /// 44 | /// 45 | /// 46 | /// 47 | /// 48 | public static object GetPropertyValue(this object obj, string propertyName, char separator = '.') 49 | { 50 | var segments = propertyName.Split(separator); 51 | object value = null; 52 | for (int i = 0; i < segments.Length; ++i) 53 | { 54 | object tmp = value ?? obj; 55 | #if NET40 56 | value = tmp.GetType().GetProperty(segments[i]).GetValue(tmp, null); 57 | #else 58 | value = tmp.GetType().GetProperty(segments[i]).GetValue(tmp); 59 | #endif 60 | } 61 | 62 | return value; 63 | } 64 | 65 | /// 66 | /// 67 | /// 68 | /// 69 | /// 70 | /// 71 | /// 72 | public static PropertyInfo GetProperty(this Type type, string propertyName, char separator) 73 | { 74 | var segments = propertyName.Split(separator); 75 | 76 | PropertyInfo propertyInfo = null; 77 | for (int i = 0; i < segments.Length; ++i) 78 | { 79 | propertyInfo = type.GetProperty(segments[i], BindingFlags.Public | BindingFlags.Instance); 80 | if (propertyInfo == null) 81 | return null; 82 | type = propertyInfo.PropertyType; 83 | } 84 | 85 | return propertyInfo; 86 | } 87 | 88 | /// 89 | /// 90 | /// 91 | /// 92 | /// 93 | /// 94 | public static bool IsNullable(this Type type, out Type argumentType) 95 | { 96 | argumentType = null; 97 | var isNullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); 98 | if (isNullable) 99 | { 100 | argumentType = type.GetGenericArguments()[0]; 101 | } 102 | 103 | return isNullable; 104 | } 105 | 106 | /// 107 | /// Find all derived types from assembly. 108 | /// If assembly is not given, given type assembly is used. 109 | /// 110 | /// 111 | /// 112 | /// 113 | public static Type[] GetDerivedTypes(this Type type, Assembly assembly = null) 114 | { 115 | return type.GetDerivedTypes(false); 116 | } 117 | 118 | /// 119 | /// Find all derived types from assembly. 120 | /// If assembly is not given, given type assembly is used. 121 | /// 122 | /// 123 | /// 124 | /// 125 | /// 126 | public static Type[] GetDerivedTypes(this Type type, bool includeItself, Assembly assembly = null) 127 | { 128 | if (assembly == null) 129 | { 130 | assembly = type.Assembly; 131 | } 132 | 133 | return assembly 134 | .GetTypes() 135 | .Where(t => (includeItself || t != type) && type.IsAssignableFrom(t)) 136 | .ToArray(); 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/TestContext.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations.Schema; 2 | using System.Data.Entity; 3 | using EntityFramework.Metadata.Test.CodeFirst.Domain; 4 | using EntityFramework.Metadata.Test.CodeFirst.Domain.ComplexTypes; 5 | 6 | namespace EntityFramework.Metadata.Test.CodeFirst 7 | { 8 | public class TestContext : DbContext 9 | { 10 | public TestContext() : base("TestContext") 11 | { 12 | 13 | } 14 | 15 | public TestContext(string connectionStringName) : base(connectionStringName) 16 | { 17 | 18 | } 19 | 20 | private const string ContractDiscriminator = "__typeid"; 21 | 22 | 23 | public DbSet Users { get; set; } 24 | 25 | public DbSet Pages { get; set; } 26 | 27 | public DbSet PageTranslations { get; set; } 28 | 29 | public DbSet WorkerTpts { get; set; } 30 | public DbSet ManagerTpts { get; set; } 31 | 32 | public DbSet EmployeeTphs { get; set; } 33 | public DbSet ManagerTphs { get; set; } 34 | public DbSet AWorkerTphs { get; set; } 35 | 36 | public DbSet ContractBases { get; set; } 37 | public DbSet Contracts { get; set; } 38 | public DbSet FixedContracts { get; set; } 39 | public DbSet StockContracts { get; set; } 40 | public DbSet K1Contracts { get; set; } 41 | public DbSet K2Contracts { get; set; } 42 | 43 | public DbSet Foos { get; set; } 44 | public DbSet EntityWithMappedPks { get; set; } 45 | 46 | public DbSet Houses { get; set; } 47 | 48 | public DbSet Companies { get; set; } 49 | 50 | protected override void OnModelCreating(DbModelBuilder mb) 51 | { 52 | mb.ComplexType(); 53 | mb.ComplexType
(); 54 | 55 | mb.Entity().ToTable("FOO", "dbx"); 56 | 57 | mb.Entity().ToTable("Users"); 58 | mb.Entity().Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 59 | mb.Entity().Property(x => x.FirstName).HasColumnName("Name"); 60 | mb.Entity().Ignore(x => x.FullName); 61 | 62 | mb.Entity().HasKey(x => x.PageId); 63 | mb.Entity().Property(x => x.Title).HasMaxLength(255).IsRequired(); 64 | mb.Entity().HasOptional(x => x.Parent).WithMany().HasForeignKey(x => x.ParentId); 65 | mb.Entity().HasRequired(x => x.CreatedBy).WithMany().HasForeignKey(x => x.CreatedById); 66 | mb.Entity().HasOptional(x => x.ModifiedBy).WithMany().HasForeignKey(x => x.ModifiedById); 67 | 68 | mb.Entity().HasKey(x => new {x.PageId, x.Language}); 69 | mb.Entity().Property(x => x.Title).HasMaxLength(255); 70 | 71 | mb.Entity().Property(x => x.Title).HasColumnName("JobTitle"); 72 | mb.Entity().Ignore(x => x.NameWithTitle); 73 | mb.Entity() 74 | .Map(x => x.ToTable("Employees")) 75 | .Map(m => m.Requires("__employeeType").HasValue(1)) 76 | .Map(m => m.Requires("__employeeType").HasValue(2)); 77 | 78 | mb.Entity().HasRequired(x => x.Boss).WithMany(x => x.Henchmen).HasForeignKey(x => x.BossId); 79 | 80 | mb.Entity().HasRequired(x => x.Boss).WithMany(x => x.Henchmen); 81 | mb.Entity().HasOptional(x => x.Referee).WithMany().HasForeignKey(x => x.RefereeId); 82 | 83 | 84 | mb.Entity().HasRequired(x => x.MeteringPoint).WithMany().HasForeignKey(x => x.MeteringPointId).WillCascadeOnDelete(false); 85 | mb.Entity().Property(x => x.PackageName).HasMaxLength(50); 86 | mb.Entity().Property(x => x.ContractNr).HasMaxLength(50); 87 | mb.Entity().Property(x => x.AvpContractNr).HasMaxLength(50); 88 | 89 | mb.Entity() 90 | .Map(x => x.ToTable("Contracts")) 91 | .Map(x => x.Requires(ContractDiscriminator).HasValue(0)) 92 | .Map(x => x.Requires(ContractDiscriminator).HasValue(1)) 93 | .Map(x => x.Requires(ContractDiscriminator).HasValue(2)) 94 | .Map(x => x.Requires(ContractDiscriminator).HasValue(3)) 95 | .Map(x => x.Requires(ContractDiscriminator).HasValue(4)); 96 | 97 | mb.Entity().Property(x => x.Margin).HasPrecision(18, 6); 98 | 99 | mb.Entity().Property(x => x.Base).HasPrecision(18, 4); 100 | mb.Entity().Property(x => x.StockMargin).HasPrecision(18, 6).HasColumnName("Margin1"); 101 | mb.Entity().Property(x => x.FixPricesJson).HasColumnName("PricesJson1"); 102 | 103 | mb.Entity().Property(x => x.Part1Margin).HasPrecision(18, 6); 104 | mb.Entity().Property(x => x.Part2Margin).HasPrecision(18, 6); 105 | 106 | 107 | mb.Entity().HasKey(k => k.BancoId); 108 | 109 | mb.Entity().Property(p => p.BancoId).HasColumnName("BancoID").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); 110 | mb.Entity().Property(p => p.Nombre).HasColumnName("Nombre").IsRequired(); 111 | 112 | mb.Entity().ToTable("EntityWithMapping"); 113 | 114 | base.OnModelCreating(mb); 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/TestHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using NUnit.Framework; 3 | 4 | namespace EntityFramework.Metadata.Test.CodeFirst 5 | { 6 | public static class TestHelper 7 | { 8 | public static IPropertyMap HasColumnName(this IPropertyMap c, string columnName) 9 | { 10 | string message = string.Format("Property {0} should be mapped to col {1}, but was mapped to {2}", c.PropertyName, columnName, c.ColumnName); 11 | Assert.AreEqual(columnName, c.ColumnName, message); 12 | return c; 13 | } 14 | 15 | public static IPropertyMap IsPk(this IPropertyMap c, bool isPk = true) 16 | { 17 | string message = string.Format("Property {0} pk flag should be {1}, but was {2}", c.PropertyName, isPk, c.IsPk); 18 | Assert.AreEqual(isPk, c.IsPk, message); 19 | return c; 20 | } 21 | 22 | public static IPropertyMap IsFk(this IPropertyMap c, bool isFk = true) 23 | { 24 | string message = string.Format("Property {0} fk flag should be {1}, but was {2}", c.PropertyName, isFk, c.IsFk); 25 | Assert.AreEqual(isFk, c.IsFk, message); 26 | return c; 27 | } 28 | 29 | public static IPropertyMap FixedLength(this IPropertyMap c, bool fixedLength = true) 30 | { 31 | string message = string.Format("Property {0} fixedLength flag should be {1}, but was {2}", c.PropertyName, fixedLength, c.FixedLength); 32 | Assert.AreEqual(fixedLength, c.FixedLength, message); 33 | return c; 34 | } 35 | 36 | public static IPropertyMap Unicode(this IPropertyMap c, bool unicode = true) 37 | { 38 | string message = string.Format("Property {0} unicode flag should be {1}, but was {2}", c.PropertyName, unicode, c.Unicode); 39 | Assert.AreEqual(unicode, c.Unicode, message); 40 | return c; 41 | } 42 | 43 | public static IPropertyMap IsNavigationProperty(this IPropertyMap c, bool isNavProp = true) 44 | { 45 | string message = string.Format("Property {0} navigationProperty flag should be {1}, but was {2}", c.PropertyName, isNavProp, c.IsNavigationProperty); 46 | Assert.AreEqual(isNavProp, c.IsNavigationProperty, message); 47 | return c; 48 | } 49 | 50 | public static IPropertyMap MaxLength(this IPropertyMap c, int maxLength) 51 | { 52 | string message = string.Format("Property {0} max length should be {1}, but was {2}", c.PropertyName, maxLength, c.MaxLength); 53 | Assert.AreEqual(maxLength, c.MaxLength, message); 54 | return c; 55 | } 56 | 57 | public static IPropertyMap NavigationPropertyName(this IPropertyMap c, string navigationProperty) 58 | { 59 | string message = string.Format("Property {0} navigation property should be '{1}', but was '{2}'", c.PropertyName, navigationProperty, c.NavigationPropertyName); 60 | Assert.AreEqual(navigationProperty, c.NavigationPropertyName, message); 61 | return c; 62 | } 63 | 64 | public static IPropertyMap ForeignKeyPropertyName(this IPropertyMap c, string fkProperty) 65 | { 66 | string message = string.Format("Property {0} fk property should be '{1}', but was '{2}'", c.PropertyName, fkProperty, c.ForeignKeyPropertyName); 67 | Assert.AreEqual(fkProperty, c.ForeignKeyPropertyName, message); 68 | return c; 69 | } 70 | 71 | public static IPropertyMap ForeignKey(this IPropertyMap c, IPropertyMap fk) 72 | { 73 | string message = string.Format("Property {0} fk does not match", c.PropertyName); 74 | Assert.AreEqual(fk, c.ForeignKey, message); 75 | return c; 76 | } 77 | 78 | public static IPropertyMap NavigationProperty(this IPropertyMap c, IPropertyMap navigationProperty) 79 | { 80 | string message = string.Format("Property {0} navigation property does not match", c.PropertyName); 81 | Assert.AreEqual(navigationProperty, c.NavigationProperty, message); 82 | return c; 83 | } 84 | 85 | public static IPropertyMap IsDiscriminator(this IPropertyMap c, bool isDiscriminator = true) 86 | { 87 | string message = string.Format("Property {0} discriminator flag should be '{1}', but was '{2}'", c.PropertyName, isDiscriminator, c.IsDiscriminator); 88 | Assert.AreEqual(isDiscriminator, c.IsDiscriminator, message); 89 | return c; 90 | } 91 | 92 | public static IPropertyMap IsIdentity(this IPropertyMap c, bool isIdentity = true) 93 | { 94 | string message = string.Format("Property {0} identity flag should be '{1}', but was '{2}'", c.PropertyName, isIdentity, c.IsIdentity); 95 | Assert.AreEqual(isIdentity, c.IsIdentity, message); 96 | return c; 97 | } 98 | 99 | public static IPropertyMap IsRequired(this IPropertyMap c, bool isRequired = true) 100 | { 101 | string message = string.Format("Property {0} required flag should be '{1}', but was '{2}'", c.PropertyName, isRequired, c.IsRequired); 102 | Assert.AreEqual(isRequired, c.IsRequired, message); 103 | return c; 104 | } 105 | 106 | public static IPropertyMap HasPrecision(this IPropertyMap c, byte precision) 107 | { 108 | string message = string.Format("Property {0} precision should be '{1}', but was '{2}'", c.PropertyName, precision, c.Precision); 109 | Assert.AreEqual(precision, c.Precision, message); 110 | return c; 111 | } 112 | 113 | public static IPropertyMap HasScale(this IPropertyMap c, byte scale) 114 | { 115 | string message = string.Format("Property {0} scale should be '{1}', but was '{2}'", c.PropertyName, scale, c.Scale); 116 | Assert.AreEqual(scale, c.Scale, message); 117 | return c; 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Mappings/EntityMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Data.Entity.Core.Metadata.Edm; 7 | 8 | namespace EntityFramework.Metadata.Mappings 9 | { 10 | internal class EntityMap : EntityMap, IEntityMap 11 | { 12 | public IPropertyMap Prop(Expression> predicate) 13 | { 14 | var predicateString = predicate.ToString(); 15 | var i = predicateString.IndexOf('.'); 16 | var propName = predicateString.Substring(i + 1); 17 | return base[propName]; 18 | } 19 | } 20 | 21 | /// 22 | /// 23 | /// 24 | internal class EntityMap : IEntityMap 25 | { 26 | private readonly Dictionary _propertyMaps = new Dictionary(); 27 | private readonly List _fks = new List(); 28 | private readonly List _pks = new List(); 29 | private readonly List _discriminators = new List(); 30 | 31 | /// 32 | /// Entity type full name 33 | /// 34 | public string TypeFullName { get; internal set; } 35 | 36 | /// 37 | /// Entity type 38 | /// 39 | public Type Type { get; internal set; } 40 | 41 | /// 42 | /// Table name in database 43 | /// 44 | public string TableName { get; internal set; } 45 | 46 | /// 47 | /// Database schema 48 | /// 49 | public string Schema { get; internal set; } 50 | 51 | /// 52 | /// Is table-per-hierarchy mapping 53 | /// 54 | public bool IsTph { get; internal set; } 55 | 56 | /// 57 | /// Is table-per-hierarchy base entity 58 | /// 59 | public bool IsRoot { get; internal set; } 60 | 61 | /// 62 | /// Column mappings for table 63 | /// 64 | public IPropertyMap[] Properties 65 | { 66 | get { return _propertyMaps.Values.ToArray(); } 67 | } 68 | 69 | /// 70 | /// Foreign key properties 71 | /// 72 | public IPropertyMap[] Fks 73 | { 74 | get { return _fks.ToArray(); } 75 | } 76 | 77 | /// 78 | /// Primary key properties 79 | /// 80 | public IPropertyMap[] Pks 81 | { 82 | get { return _pks.ToArray(); } 83 | } 84 | 85 | /// 86 | /// Tph entity discriminators 87 | /// 88 | public IPropertyMap[] Discriminators 89 | { 90 | get { return _discriminators.ToArray(); } 91 | } 92 | 93 | /// 94 | /// Gets property map by property name 95 | /// 96 | /// 97 | /// 98 | public IPropertyMap this[string propertyName] 99 | { 100 | get 101 | { 102 | if (!_propertyMaps.ContainsKey(propertyName)) 103 | { 104 | return null; 105 | } 106 | 107 | return _propertyMaps[propertyName]; 108 | } 109 | } 110 | 111 | /// 112 | /// Gets property map by property name 113 | /// 114 | /// 115 | /// 116 | public IPropertyMap Prop(string propertyName) 117 | { 118 | return this[propertyName]; 119 | } 120 | 121 | /// 122 | /// Parent DbMapping 123 | /// 124 | //public IDbMapping DbMapping { get; internal set; } 125 | 126 | /// 127 | /// 128 | /// 129 | internal EdmType ParentEdmType { get; set; } 130 | 131 | /// 132 | /// 133 | /// 134 | internal EdmType EdmType { get; set; } 135 | 136 | /* 137 | /// 138 | /// 139 | /// 140 | /// 141 | /// 142 | /// 143 | internal TableMapping(string typeFullName, string tableName, string schema) 144 | { 145 | TypeFullName = typeFullName; 146 | TableName = tableName; 147 | Schema = schema; 148 | 149 | Type = TryGetRefObjectType(); 150 | } 151 | */ 152 | 153 | 154 | /// 155 | /// 156 | /// 157 | /// 158 | /// 159 | /// 160 | public PropertyMap MapProperty(string property, string columnName) 161 | { 162 | var cmap = new PropertyMap(property, columnName) { EntityMap = this }; 163 | var propNames = property.Split('.'); 164 | 165 | var x = Expression.Parameter(Type, "x"); 166 | Expression propertyExpression = Expression.PropertyOrField(x, propNames[0]); 167 | propertyExpression = propNames.Skip(1).Aggregate(propertyExpression, Expression.PropertyOrField); 168 | 169 | var expression = Expression.Lambda(Expression.Convert(propertyExpression, typeof (object)), x); 170 | cmap.Type = propertyExpression.Type; 171 | 172 | var selector = expression.Compile(); 173 | cmap.Selector = selector; 174 | 175 | _propertyMaps.Add(property, cmap); 176 | return cmap; 177 | } 178 | 179 | public PropertyMap MapDiscriminator(string name, object defaultValue) 180 | { 181 | var cmap = new PropertyMap(name, name) { EntityMap = this, IsDiscriminator = true, DefaultValue = defaultValue }; 182 | 183 | var x = Expression.Parameter(Type, "x"); 184 | var expression = Expression.Lambda(Expression.Convert(Expression.Constant(defaultValue), typeof(object)), x); 185 | var selector = expression.Compile(); 186 | cmap.Selector = selector; 187 | 188 | _propertyMaps.Add(name, cmap); 189 | return cmap; 190 | } 191 | 192 | public void AddFk(PropertyMap colMapping) 193 | { 194 | _fks.Add(colMapping); 195 | } 196 | 197 | public void AddPk(PropertyMap colMapping) 198 | { 199 | _pks.Add(colMapping); 200 | } 201 | 202 | public void AddDiscriminator(PropertyMap propertyMap) 203 | { 204 | _discriminators.Add(propertyMap); 205 | } 206 | } 207 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/EntityFramework.Metadata.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C9530AA5-ACAD-4892-96E8-987A7847C341} 8 | Library 9 | Properties 10 | EntityFramework.Metadata.Test 11 | EntityFramework.Metadata.Test 12 | v4.0 13 | 512 14 | 15 | SAK 16 | SAK 17 | SAK 18 | SAK 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | TRACE;DEBUG;NET40 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | False 40 | ..\packages\EntityFramework.6.1.3\lib\net40\EntityFramework.dll 41 | 42 | 43 | ..\packages\EntityFramework.6.1.3\lib\net40\EntityFramework.SqlServer.dll 44 | 45 | 46 | 47 | 48 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Code 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | TestModel.tt 85 | 86 | 87 | 88 | TestModel.tt 89 | 90 | 91 | True 92 | True 93 | TestModel.Context.tt 94 | 95 | 96 | True 97 | True 98 | TestModel.tt 99 | 100 | 101 | True 102 | True 103 | TestModel.edmx 104 | 105 | 106 | 107 | 108 | 109 | Designer 110 | 111 | 112 | EntityModelCodeGenerator 113 | TestModel.Designer.cs 114 | 115 | 116 | TextTemplatingFileGenerator 117 | TestModel.edmx 118 | TestModel.Context.cs 119 | 120 | 121 | TestModel.edmx 122 | 123 | 124 | TextTemplatingFileGenerator 125 | TestModel.edmx 126 | TestModel.cs 127 | 128 | 129 | Designer 130 | 131 | 132 | 133 | 134 | {609BE91A-F11B-49BC-98D9-F7C83517A59E} 135 | EntityFramework.Metadata 136 | 137 | 138 | 139 | 140 | 141 | 142 | 149 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/TphTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EntityFramework.Metadata.Extensions; 3 | using EntityFramework.Metadata.Test.CodeFirst.Domain; 4 | using NUnit.Framework; 5 | 6 | namespace EntityFramework.Metadata.Test.CodeFirst 7 | { 8 | public class TphTest : TestBase 9 | { 10 | [Test] 11 | public void Employee_BaseClass() 12 | { 13 | using (var ctx = GetContext()) 14 | { 15 | var map = ctx.Db(); 16 | Console.WriteLine("{0}:{1}", map.Type, map.TableName); 17 | 18 | map.Prop(x => x.Id) 19 | .HasColumnName("Id") 20 | .IsPk() 21 | .IsFk(false); 22 | 23 | map.Prop(x => x.Name) 24 | .HasColumnName("Name") 25 | .MaxLength(NvarcharMax) 26 | .NavigationPropertyName(null) 27 | .IsPk(false) 28 | .IsFk(false); 29 | 30 | Assert.IsNull(map.Prop(x => x.NameWithTitle)); 31 | 32 | map.Prop(x => x.Title) 33 | .HasColumnName("JobTitle") 34 | .IsPk(false) 35 | .IsFk(false) 36 | .MaxLength(NvarcharMax) 37 | .IsNavigationProperty(false); 38 | 39 | map.Prop("__employeeType") 40 | .HasColumnName("__employeeType") 41 | .IsPk(false) 42 | .IsFk(false) 43 | .IsDiscriminator(); 44 | } 45 | } 46 | 47 | [Test] 48 | public void Employee_DerivedType_First() 49 | { 50 | using (var ctx = GetContext()) 51 | { 52 | var map = ctx.Db(); 53 | Console.WriteLine("{0}:{1}", map.Type, map.TableName); 54 | 55 | map.Prop(x => x.Id) 56 | .HasColumnName("Id"); 57 | 58 | map.Prop(x => x.Name) 59 | .HasColumnName("Name"); 60 | 61 | map.Prop(x => x.Title) 62 | .HasColumnName("JobTitle"); 63 | 64 | map.Prop(x => x.Boss) 65 | .IsNavigationProperty() 66 | .ForeignKeyPropertyName("BossId") 67 | .ForeignKey(map.Prop(x => x.BossId)) 68 | .IsFk(false) 69 | .HasColumnName("BossId"); 70 | 71 | map.Prop(x => x.BossId) 72 | .IsFk() 73 | .NavigationPropertyName("Boss") 74 | .NavigationProperty(map.Prop(x => x.Boss)) 75 | .HasColumnName("BossId"); 76 | 77 | map.Prop(x => x.RefId) 78 | .HasColumnName("RefId"); 79 | 80 | Assert.AreEqual(1, map.Discriminators.Length); 81 | Assert.AreEqual("__employeeType", map.Discriminators[0].ColumnName); 82 | } 83 | } 84 | 85 | [Test] 86 | public void Employee_DerivedType_NotFirst() 87 | { 88 | using (var ctx = GetContext()) 89 | { 90 | var map = ctx.Db(); 91 | Console.WriteLine("{0}:{1}", map.Type, map.TableName); 92 | 93 | map.Prop(x => x.Id) 94 | .HasColumnName("Id"); 95 | 96 | map.Prop(x => x.Name) 97 | .HasColumnName("Name"); 98 | 99 | map.Prop(x => x.Title) 100 | .HasColumnName("JobTitle"); 101 | 102 | map.Prop(x => x.Rank) 103 | .HasColumnName("Rank"); 104 | 105 | map.Prop(x => x.RefId) 106 | .HasColumnName("RefId1"); 107 | 108 | Assert.AreEqual(1, map.Discriminators.Length); 109 | Assert.AreEqual("__employeeType", map.Discriminators[0].ColumnName); 110 | } 111 | } 112 | 113 | [Test] 114 | public void Contract_ContractBase() 115 | { 116 | using (var ctx = new TestContext()) 117 | { 118 | var map = ctx.Db(); 119 | 120 | var columns = map.Properties; 121 | Assert.AreEqual(19, columns.Length); 122 | 123 | Assert.AreEqual(1, map.Discriminators.Length); 124 | } 125 | } 126 | 127 | [Test] 128 | public void Contract_Contract() 129 | { 130 | using (var ctx = new TestContext()) 131 | { 132 | var map = ctx.Db(); 133 | 134 | var columns = map.Properties; 135 | Assert.AreEqual(19, columns.Length); 136 | 137 | map.Prop(x => x.Id) 138 | .IsIdentity() 139 | .IsFk(false) 140 | .IsDiscriminator(false) 141 | .IsRequired() 142 | .HasColumnName("Id"); 143 | 144 | 145 | map.Prop(x => x.AvpContractNr) 146 | .IsIdentity(false) 147 | .IsFk(false) 148 | .IsDiscriminator(false) 149 | .IsRequired(false) 150 | .HasColumnName("AvpContractNr") 151 | .MaxLength(50); 152 | } 153 | } 154 | 155 | [Test] 156 | public void Contract_ContractFixed() 157 | { 158 | using (var ctx = new TestContext()) 159 | { 160 | var tableMapping = ctx.Db(); 161 | 162 | var columns = tableMapping.Properties; 163 | Assert.AreEqual(21, columns.Length); 164 | } 165 | } 166 | 167 | [Test] 168 | public void Contract_ContractStock() 169 | { 170 | using (var ctx = new TestContext()) 171 | { 172 | var tableMapping = ctx.Db(); 173 | 174 | var columns = tableMapping.Properties; 175 | Assert.AreEqual(21, columns.Length); 176 | } 177 | } 178 | 179 | [Test] 180 | public void Contract_ContractKomb1() 181 | { 182 | using (var ctx = new TestContext()) 183 | { 184 | var tableMapping = ctx.Db(); 185 | 186 | var columns = tableMapping.Properties; 187 | Assert.AreEqual(24, columns.Length); 188 | 189 | tableMapping.Prop(x => x.Base) 190 | .HasColumnName("Base") 191 | .HasPrecision(18) 192 | .HasScale(4); 193 | } 194 | } 195 | 196 | [Test] 197 | public void Contract_ContractKomb2() 198 | { 199 | using (var ctx = new TestContext()) 200 | { 201 | var tableMapping = ctx.Db(); 202 | 203 | var columns = tableMapping.Properties; 204 | Assert.AreEqual(26, columns.Length); 205 | } 206 | } 207 | } 208 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/EntityFramework.Metadata.Test.Net45.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BC13B004-21FE-4A7A-8B8B-728E4A9CEC67} 8 | Library 9 | Properties 10 | EntityFramework.Metadata.Test 11 | EntityFramework.Metadata.Test 12 | v4.5 13 | 512 14 | SAK 15 | SAK 16 | SAK 17 | SAK 18 | 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | TRACE;DEBUG;EF6 EF61 26 | prompt 27 | 4 28 | false 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE;EF6 EF61 35 | prompt 36 | 4 37 | false 38 | 39 | 40 | 41 | False 42 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 43 | 44 | 45 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 46 | 47 | 48 | 49 | 50 | ..\packages\NUnit.2.6.4\lib\nunit.framework.dll 51 | True 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | TestModel.tt 86 | 87 | 88 | 89 | TestModel.tt 90 | 91 | 92 | True 93 | True 94 | TestModel.Context.tt 95 | 96 | 97 | True 98 | True 99 | TestModel.tt 100 | 101 | 102 | TestModel.cs 103 | 104 | 105 | True 106 | True 107 | TestModel.tt 108 | 109 | 110 | True 111 | True 112 | TestModel.edmx 113 | 114 | 115 | 116 | 117 | 118 | 119 | EntityModelCodeGenerator 120 | TestModel1.Designer.cs 121 | 122 | 123 | TextTemplatingFileGenerator 124 | TestModel.edmx 125 | TestModel.Context.cs 126 | 127 | 128 | TestModel.edmx 129 | 130 | 131 | TextTemplatingFileGenerator 132 | TestModel.edmx 133 | TestModel.cs 134 | 135 | 136 | 137 | 138 | 139 | {168dd262-c4db-49e8-bf62-bfdd3b6da41e} 140 | EntityFramework.Metadata.Net45 141 | 142 | 143 | 144 | 145 | 146 | 147 | 154 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/TestModel.edmx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/CodeFirst/MappingTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using EntityFramework.Metadata.Extensions; 4 | using EntityFramework.Metadata.Test.CodeFirst.Domain; 5 | using NUnit.Framework; 6 | 7 | namespace EntityFramework.Metadata.Test.CodeFirst 8 | { 9 | [TestFixture] 10 | public class MappingTest : TestBase 11 | { 12 | [Test] 13 | public void TableNames() 14 | { 15 | using (var ctx = GetContext()) 16 | { 17 | var sw = new Stopwatch(); 18 | sw.Restart(); 19 | var dbmapping = ctx.Db(); 20 | sw.Start(); 21 | 22 | Console.WriteLine("Mapping took: {0}ms", sw.Elapsed.TotalMilliseconds); 23 | 24 | foreach (var tableMapping in dbmapping) 25 | { 26 | Console.WriteLine("{0}: {1}.{2}", tableMapping.Type.FullName, tableMapping.Schema, 27 | tableMapping.TableName); 28 | } 29 | 30 | Assert.AreEqual(ctx.Db().TableName, "Pages"); 31 | Assert.AreEqual(ctx.Db().Schema, "dbo"); 32 | Assert.AreEqual(ctx.Db().TableName, "PageTranslations"); 33 | 34 | Assert.AreEqual(ctx.Db().TableName, "Users"); 35 | 36 | Assert.AreEqual(ctx.Db().TableName, "MeteringPoints"); 37 | 38 | Assert.AreEqual(ctx.Db().TableName, "Employees"); 39 | Assert.AreEqual(ctx.Db().TableName, "Employees"); 40 | Assert.AreEqual(ctx.Db().TableName, "Employees"); 41 | 42 | Assert.AreEqual(ctx.Db().TableName, "Contracts"); 43 | Assert.AreEqual(ctx.Db().TableName, "Contracts"); 44 | Assert.AreEqual(ctx.Db().TableName, "Contracts"); 45 | Assert.AreEqual(ctx.Db().TableName, "Contracts"); 46 | Assert.AreEqual(ctx.Db().TableName, "Contracts"); 47 | Assert.AreEqual(ctx.Db().TableName, "Contracts"); 48 | 49 | Assert.AreEqual(ctx.Db().TableName, "WorkerTPTs"); 50 | Assert.AreEqual(ctx.Db().TableName, "ManagerTPTs"); 51 | 52 | Assert.AreEqual(ctx.Db().TableName, "FOO"); 53 | Assert.AreEqual(ctx.Db().Schema, "dbx"); 54 | } 55 | } 56 | 57 | [Test] 58 | public void Entity_WithMappedPk() 59 | { 60 | using (var ctx = GetContext()) 61 | { 62 | var map = ctx.Db(); 63 | Console.WriteLine("{0}:{1}", map.Type, map.TableName); 64 | 65 | map.Prop(x => x.BancoId) 66 | .HasColumnName("BancoID") 67 | .IsPk() 68 | .IsFk(false) 69 | .IsRequired() 70 | .IsIdentity(false) 71 | .IsNavigationProperty(false); 72 | } 73 | } 74 | 75 | [Test] 76 | public void Entity_ComplexType() 77 | { 78 | using (var ctx = new TestContext()) 79 | { 80 | var map = ctx.Db(); 81 | 82 | map.Prop(x => x.Id) 83 | .HasColumnName("Id") 84 | .IsPk() 85 | .IsFk(false) 86 | .IsNavigationProperty(false); 87 | 88 | map.Prop(x => x.FirstName) 89 | .HasColumnName("Name") 90 | .IsPk(false) 91 | .IsFk(false) 92 | .IsNavigationProperty(false) 93 | .MaxLength(NvarcharMax); 94 | 95 | map.Prop(x => x.LastName) 96 | .HasColumnName("LastName") 97 | .IsPk(false) 98 | .IsFk(false) 99 | .IsNavigationProperty(false) 100 | .MaxLength(NvarcharMax); 101 | 102 | map.Prop(x => x.Contact.PhoneNumber) 103 | .HasColumnName("Contact_PhoneNumber") 104 | .IsPk(false) 105 | .IsFk(false) 106 | .IsNavigationProperty(false) 107 | .MaxLength(NvarcharMax); 108 | 109 | map.Prop(x => x.Contact.BusinessAddress.StreetAddress) 110 | .HasColumnName("Contact_BusinessAddress_StreetAddress") 111 | .IsPk(false) 112 | .IsFk(false) 113 | .IsNavigationProperty(false) 114 | .MaxLength(NvarcharMax); 115 | 116 | map.Prop(x => x.Contact.BusinessAddress.County) 117 | .HasColumnName("Contact_BusinessAddress_County") 118 | .IsPk(false) 119 | .IsFk(false) 120 | .IsNavigationProperty(false) 121 | .MaxLength(NvarcharMax); 122 | 123 | map.Prop(x => x.Contact.BusinessAddress.Country) 124 | .HasColumnName("Contact_BusinessAddress_Country") 125 | .IsPk(false) 126 | .IsFk(false) 127 | .IsNavigationProperty(false) 128 | .MaxLength(NvarcharMax); 129 | 130 | map.Prop(x => x.Contact.BusinessAddress.City) 131 | .HasColumnName("Contact_BusinessAddress_City") 132 | .IsPk(false) 133 | .IsFk(false) 134 | .IsNavigationProperty(false) 135 | .MaxLength(NvarcharMax); 136 | 137 | map.Prop(x => x.Contact.BusinessAddress.PostalCode) 138 | .HasColumnName("Contact_BusinessAddress_PostalCode") 139 | .IsPk(false) 140 | .IsFk(false) 141 | .IsNavigationProperty(false) 142 | .MaxLength(NvarcharMax); 143 | 144 | map.Prop(x => x.Contact.ShippingAddress.StreetAddress) 145 | .HasColumnName("Contact_ShippingAddress_StreetAddress") 146 | .IsPk(false) 147 | .IsFk(false) 148 | .IsNavigationProperty(false) 149 | .MaxLength(NvarcharMax); 150 | 151 | map.Prop(x => x.Contact.ShippingAddress.Country) 152 | .HasColumnName("Contact_ShippingAddress_Country") 153 | .IsPk(false) 154 | .IsFk(false) 155 | .IsNavigationProperty(false) 156 | .MaxLength(NvarcharMax); 157 | 158 | map.Prop(x => x.Contact.ShippingAddress.County) 159 | .HasColumnName("Contact_ShippingAddress_County") 160 | .IsPk(false) 161 | .IsFk(false) 162 | .IsNavigationProperty(false) 163 | .MaxLength(NvarcharMax); 164 | 165 | map.Prop(x => x.Contact.ShippingAddress.City) 166 | .HasColumnName("Contact_ShippingAddress_City") 167 | .IsPk(false) 168 | .IsFk(false) 169 | .IsNavigationProperty(false) 170 | .MaxLength(NvarcharMax); 171 | 172 | map.Prop(x => x.Contact.ShippingAddress.PostalCode) 173 | .HasColumnName("Contact_ShippingAddress_PostalCode") 174 | .IsPk(false) 175 | .IsFk(false) 176 | .IsNavigationProperty(false) 177 | .MaxLength(NvarcharMax); 178 | #if !NET40 179 | var propertyPropertyMap = map.Prop(x => x.Contact.ShippingAddress.Location); 180 | propertyPropertyMap 181 | .HasColumnName("Contact_ShippingAddress_Location") 182 | .IsPk(false) 183 | .IsFk(false) 184 | .IsNavigationProperty(false); 185 | 186 | Console.WriteLine(propertyPropertyMap.DefaultValue); 187 | Console.WriteLine(propertyPropertyMap.FixedLength); 188 | Console.WriteLine(propertyPropertyMap.MaxLength); 189 | Console.WriteLine(propertyPropertyMap.Precision); 190 | Console.WriteLine(propertyPropertyMap.Scale); 191 | Console.WriteLine(propertyPropertyMap.Type); 192 | Console.WriteLine(propertyPropertyMap.Unicode); 193 | 194 | var shapePropertyMap = map.Prop(x => x.Contact.ShippingAddress.Shape); 195 | shapePropertyMap 196 | .HasColumnName("Contact_ShippingAddress_Shape") 197 | .IsPk(false) 198 | .IsFk(false) 199 | .IsNavigationProperty(false); 200 | 201 | Console.WriteLine(shapePropertyMap.DefaultValue); 202 | Console.WriteLine(shapePropertyMap.FixedLength); 203 | Console.WriteLine(shapePropertyMap.MaxLength); 204 | Console.WriteLine(shapePropertyMap.Precision); 205 | Console.WriteLine(shapePropertyMap.Scale); 206 | Console.WriteLine(shapePropertyMap.Type); 207 | Console.WriteLine(shapePropertyMap.Unicode); 208 | #endif 209 | } 210 | } 211 | 212 | [Test] 213 | public void Entity_ComplextType_WhereComplexTypeIsLastProperty() 214 | { 215 | using (var ctx = new TestContext()) 216 | { 217 | var map = ctx.Db(); 218 | 219 | map.Prop(x => x.Name) 220 | .HasColumnName("Name"); 221 | 222 | map.Prop(x => x.Address.Country) 223 | .HasColumnName("Address_Country"); 224 | } 225 | } 226 | 227 | [Test] 228 | public void Entity_ComplexType_WhereTwoComplexTypesAreAdjacent() 229 | { 230 | using (var ctx = new TestContext()) 231 | { 232 | var map = ctx.Db(); 233 | 234 | map.Prop(x => x.FirstContact.BusinessAddress.StreetAddress) 235 | .HasColumnName("FirstContact_BusinessAddress_StreetAddress"); 236 | 237 | map.Prop(x => x.FirstContact.ShippingAddress.StreetAddress) 238 | .HasColumnName("FirstContact_ShippingAddress_StreetAddress"); 239 | 240 | map.Prop(x => x.BusinessAddress.StreetAddress) 241 | .HasColumnName("BusinessAddress_StreetAddress"); 242 | 243 | map.Prop(x => x.ShippingAddress.StreetAddress) 244 | .HasColumnName("ShippingAddress_StreetAddress"); 245 | } 246 | } 247 | 248 | [Test] 249 | public void Entity_TPT_WorkerTPT() 250 | { 251 | using (var ctx = GetContext()) 252 | { 253 | var map = ctx.Db(); 254 | 255 | map.Prop(x => x.Id) 256 | .IsPk() 257 | .IsFk(false) 258 | .HasColumnName("Id") 259 | .IsNavigationProperty(false); 260 | 261 | map.Prop(x => x.Name) 262 | .IsPk(false) 263 | .IsFk(false) 264 | .HasColumnName("Name") 265 | .IsNavigationProperty(false) 266 | .MaxLength(NvarcharMax); 267 | 268 | map.Prop(x => x.JobTitle) 269 | .IsPk(false) 270 | .IsFk(false) 271 | .HasColumnName("JobTitle") 272 | .IsNavigationProperty(false) 273 | .MaxLength(NvarcharMax); 274 | 275 | map.Prop(x => x.Boss) 276 | .IsPk(false) 277 | .IsFk() 278 | .HasColumnName("Boss_Id") 279 | .IsNavigationProperty(); 280 | 281 | var refereeIdProp = map.Prop(x => x.RefereeId); 282 | refereeIdProp 283 | .IsPk(false) 284 | .IsFk() 285 | .HasColumnName("RefereeId") 286 | .IsNavigationProperty(false) 287 | .NavigationPropertyName("Referee"); 288 | 289 | 290 | 291 | map.Prop(x => x.Referee) 292 | .HasColumnName("RefereeId") 293 | .IsPk(false) 294 | .IsFk(false) 295 | .IsIdentity(false) 296 | .IsNavigationProperty() 297 | .ForeignKeyPropertyName("RefereeId") 298 | .ForeignKey(refereeIdProp); 299 | } 300 | } 301 | 302 | [Test] 303 | public void Entity_TPT_ManagerTPT() 304 | { 305 | using (var ctx = GetContext()) 306 | { 307 | var map = ctx.Db(); 308 | 309 | map.Prop(x => x.Id) 310 | .IsPk() 311 | .IsFk(false) 312 | .HasColumnName("Id") 313 | .IsNavigationProperty(false); 314 | 315 | map.Prop(x => x.Name) 316 | .IsPk(false) 317 | .IsFk(false) 318 | .HasColumnName("Name") 319 | .IsNavigationProperty(false) 320 | .MaxLength(NvarcharMax); 321 | 322 | map.Prop(x => x.JobTitle) 323 | .IsPk(false) 324 | .IsFk(false) 325 | .HasColumnName("JobTitle") 326 | .IsNavigationProperty(false) 327 | .MaxLength(NvarcharMax); 328 | 329 | map.Prop(x => x.Rank) 330 | .IsPk(false) 331 | .IsFk(false) 332 | .HasColumnName("Rank") 333 | .IsNavigationProperty(false); 334 | } 335 | } 336 | 337 | [Test] 338 | public void Entity_Simple() 339 | { 340 | using (var ctx = new TestContext()) 341 | { 342 | var map = ctx.Db(); 343 | Console.WriteLine("{0}:{1}", map.Type, map.TableName); 344 | 345 | map.Prop(x => x.PageId) 346 | .HasColumnName("PageId") 347 | .IsPk() 348 | .IsFk(false) 349 | .IsIdentity() 350 | .IsNavigationProperty(false); 351 | 352 | map.Prop(x => x.Title) 353 | .HasColumnName("Title") 354 | .IsPk(false) 355 | .IsFk(false) 356 | .IsIdentity(false) 357 | .IsRequired() 358 | .IsNavigationProperty(false) 359 | .MaxLength(255); 360 | 361 | map.Prop(x => x.Content) 362 | .HasColumnName("Content") 363 | .IsPk(false) 364 | .IsFk(false) 365 | .IsIdentity(false) 366 | .IsRequired(false) 367 | .IsNavigationProperty(false) 368 | .MaxLength(NvarcharMax); 369 | 370 | map.Prop(x => x.ParentId) 371 | .HasColumnName("ParentId") 372 | .IsPk(false) 373 | .IsFk() 374 | .IsIdentity(false) 375 | .IsRequired(false) 376 | .IsNavigationProperty(false) 377 | .NavigationPropertyName("Parent"); 378 | 379 | Assert.AreEqual(map.Prop(x => x.PageId), map.Prop(x => x.ParentId).FkTargetColumn); 380 | 381 | map.Prop(x => x.Parent) 382 | .HasColumnName("ParentId") 383 | .IsPk(false) 384 | .IsFk(false) 385 | .IsIdentity(false) 386 | .IsNavigationProperty() 387 | .ForeignKeyPropertyName("ParentId") 388 | .ForeignKey(map.Prop(x => x.ParentId)); 389 | 390 | map.Prop(x => x.CreatedAt) 391 | .HasColumnName("CreatedAt") 392 | .IsPk(false) 393 | .IsFk(false) 394 | .IsIdentity(false) 395 | .IsRequired(true) 396 | .IsNavigationProperty(false); 397 | 398 | map.Prop(x => x.ModifiedAt) 399 | .HasColumnName("ModifiedAt") 400 | .IsPk(false) 401 | .IsFk(false) 402 | .IsRequired(false) 403 | .IsIdentity(false) 404 | .IsNavigationProperty(false); 405 | } 406 | } 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /src/EntityFramework.Metadata/Mappers/MapperBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | //using System.ComponentModel.DataAnnotations; 5 | //using System.Data; 6 | //using System.Data.Entity.SqlServer; 7 | using System.Linq; 8 | using System.Reflection; 9 | using EntityFramework.Metadata.Exceptions; 10 | using EntityFramework.Metadata.Extensions; 11 | using EntityFramework.Metadata.Mappings; 12 | using System.Data.Entity.Core.Metadata.Edm; 13 | 14 | namespace EntityFramework.Metadata.Mappers 15 | { 16 | internal abstract class MapperBase 17 | { 18 | /// 19 | /// 20 | /// 21 | protected EntityContainer EntityContainer { get; private set; } 22 | 23 | /// 24 | /// The magic box 25 | /// 26 | protected readonly MetadataWorkspace MetadataWorkspace; 27 | 28 | /// 29 | /// Table mappings dictionary where key is entity type full name. 30 | /// 31 | protected readonly Dictionary _entityMaps = new Dictionary(); 32 | 33 | /// 34 | /// Primary keys of tables. 35 | /// Key is table name (in db). 36 | /// Values is array of entity property names that are primary keys 37 | /// 38 | private readonly Dictionary _pks = new Dictionary(); 39 | 40 | /// 41 | /// Foreign keys map 42 | /// Key is fk ref 43 | /// Value is pk ref 44 | /// 45 | private readonly Dictionary _fks = new Dictionary(); 46 | 47 | private Dictionary _tphData; 48 | 49 | protected virtual Dictionary GetTphData() 50 | { 51 | var entitySetMaps = (IEnumerable)MetadataWorkspace 52 | .GetItemCollection(DataSpace.CSSpace)[0] 53 | .GetPrivateFieldValue("EntitySetMaps"); 54 | 55 | var data = new Dictionary(); 56 | 57 | foreach (var entitySetMap in entitySetMaps) 58 | { 59 | var props = new List(); 60 | var navProps = new List(); 61 | var discriminators = new Dictionary(); 62 | 63 | var typeMappings = (IEnumerable)entitySetMap.GetPrivateFieldValue("TypeMappings"); 64 | 65 | // make sure that the baseType appear last in the list 66 | typeMappings = typeMappings 67 | .OrderBy(tm => ((IEnumerable)tm.GetPrivateFieldValue("IsOfTypes")).Count()); 68 | 69 | foreach (var typeMapping in typeMappings) 70 | { 71 | var types = (IEnumerable)typeMapping.GetPrivateFieldValue("Types"); 72 | var isOfypes = (IEnumerable)typeMapping.GetPrivateFieldValue("IsOfTypes"); 73 | var mappingFragments = (IEnumerable)typeMapping.GetPrivateFieldValue("MappingFragments"); 74 | 75 | // if isOfType.length > 0, then it is base type of TPH 76 | // must merge properties with siblings 77 | foreach (EntityType type in isOfypes) 78 | { 79 | var identity = type.ToString(); 80 | if (!data.ContainsKey(identity)) 81 | data[identity] = new TphData(); 82 | 83 | data[identity].NavProperties = navProps.ToArray(); 84 | data[identity].Properties = props.ToArray(); 85 | data[identity].Discriminators = discriminators; 86 | } 87 | 88 | foreach (EntityType type in types) 89 | { 90 | var identity = type.ToString(); 91 | if (!data.ContainsKey(identity)) 92 | data[identity] = new TphData(); 93 | 94 | // type.Properties gets properties including inherited properties 95 | var tmp = new List(type.Properties); 96 | 97 | foreach (var navProp in type.NavigationProperties) 98 | { 99 | var associationType = navProp.RelationshipType as AssociationType; 100 | if (associationType != null) 101 | { 102 | // if entity does not contain id property i.e has only reference object for a fk 103 | if (associationType.ReferentialConstraints.Count == 0) 104 | { 105 | tmp.Add(navProp); 106 | } 107 | } 108 | } 109 | 110 | data[identity].NavProperties = type.NavigationProperties.ToArray(); 111 | data[identity].Properties = tmp.ToArray(); 112 | 113 | foreach (var prop in type.Properties) 114 | { 115 | if (!props.Contains(prop)) 116 | props.Add(prop); 117 | } 118 | 119 | foreach (var navProp in type.NavigationProperties) 120 | { 121 | if (!navProps.Contains(navProp)) 122 | navProps.Add(navProp); 123 | } 124 | 125 | foreach (var fragment in mappingFragments) 126 | { 127 | var conditionProperties = (IEnumerable)fragment.GetPrivateFieldValue("m_conditionProperties"); 128 | foreach (var conditionalProperty in conditionProperties) 129 | { 130 | var columnName = ((EdmProperty)conditionalProperty.GetPrivateFieldValue("Key")).Name; 131 | var value = conditionalProperty.GetPrivateFieldValue("Value").GetPrivateFieldValue("Value"); 132 | 133 | data[identity].Discriminators[columnName] = value; 134 | discriminators[columnName] = value; 135 | } 136 | } 137 | } 138 | } 139 | } 140 | 141 | return data; 142 | } 143 | 144 | public Dictionary TphData 145 | { 146 | get 147 | { 148 | if (_tphData != null) 149 | return _tphData; 150 | 151 | _tphData = GetTphData(); 152 | return _tphData; 153 | } 154 | } 155 | 156 | /// 157 | /// Type name and Edm.EntityType map for EF6+ 158 | /// Key is type full name (Also OSpace item fullname and OCSpace identity) 159 | /// Value is EntityType from CSpace 160 | /// 161 | /// 162 | protected virtual Dictionary GetTypeMappingsEf6() 163 | { 164 | return MetadataWorkspace.GetItems(DataSpace.OCSpace) 165 | .Select(x => new { identity = x.ToString().Split(':'), edmItem = x.GetPrivateFieldValue("EdmItem") as EntityType }) 166 | .Where(x => x.edmItem != null) 167 | .ToDictionary(x => x.identity[0], x => x.edmItem); 168 | } 169 | 170 | 171 | private Dictionary _typeMappings; 172 | 173 | /// 174 | /// Type name and Edm.EntityType map. 175 | /// Key is type full name (Also OSpace item fullname). 176 | /// Value is EntityType from CSpace. 177 | /// 178 | /// 179 | public Dictionary TypeMappings 180 | { 181 | get 182 | { 183 | if (_typeMappings != null) 184 | { 185 | return _typeMappings; 186 | } 187 | 188 | _typeMappings = GetTypeMappingsEf6(); 189 | return _typeMappings; 190 | } 191 | } 192 | 193 | /// 194 | /// 195 | /// 196 | /// 197 | /// Code first or DB first entityContainer 198 | protected MapperBase(MetadataWorkspace metadataWorkspace, EntityContainer entityContainer) 199 | { 200 | MetadataWorkspace = metadataWorkspace; 201 | EntityContainer = entityContainer; 202 | 203 | var relations = MetadataWorkspace.GetItems(DataSpace.CSpace).OfType(); 204 | 205 | foreach (var associationType in relations) 206 | { 207 | foreach (var referentialConstraint in associationType.ReferentialConstraints) 208 | { 209 | for (int i = 0; i < referentialConstraint.ToProperties.Count; ++i) 210 | { 211 | _fks[referentialConstraint.ToProperties[i]] = referentialConstraint.FromProperties[i]; 212 | } 213 | } 214 | } 215 | } 216 | 217 | /* 218 | /// 219 | /// 220 | /// 221 | /// 222 | /// 223 | /// 224 | protected abstract string GetTableName(string typeFullName, EntitySet entitySet); 225 | */ 226 | 227 | /// 228 | /// 229 | /// 230 | /// 231 | /// 232 | /// 233 | /// 234 | internal EntityMap RegEntity(string typeFullName, string tableName, string schema) 235 | { 236 | var entityType = TryGetRefObjectType(typeFullName); 237 | 238 | var entityMapType = typeof(EntityMap<>); 239 | var genericType = entityMapType.MakeGenericType(entityType); 240 | 241 | var entityMap = (EntityMap)Activator.CreateInstance(genericType); 242 | entityMap.TypeFullName = typeFullName; 243 | entityMap.TableName = tableName; 244 | entityMap.Schema = schema; 245 | entityMap.Type = entityType; 246 | 247 | _entityMaps.Add(typeFullName, entityMap); 248 | 249 | return entityMap; 250 | } 251 | 252 | /// 253 | /// 254 | /// 255 | /// 256 | private Type TryGetRefObjectType(string typeFullName) 257 | { 258 | foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) 259 | { 260 | var t = a.GetType(typeFullName); 261 | if (t != null) 262 | return t; 263 | } 264 | 265 | return null; 266 | } 267 | 268 | protected class PrepareMappingRes 269 | { 270 | public string TableName { get; set; } 271 | public EntitySet StorageEntitySet { get; set; } 272 | public bool IsRoot { get; set; } 273 | public EdmType BaseEdmType { get; set; } 274 | } 275 | 276 | protected abstract PrepareMappingRes PrepareMapping(string typeFullName, EdmType edmitem); 277 | 278 | /// 279 | /// 280 | /// 281 | /// 282 | /// 283 | public EntityMap MapEntity(string typeFullName, EdmType edmItem) 284 | { 285 | var identity = edmItem.FullName; 286 | if (!TphData.ContainsKey(identity)) 287 | { 288 | return null; 289 | } 290 | 291 | var temp = PrepareMapping(typeFullName, edmItem); 292 | if (temp == null) 293 | { 294 | return null; 295 | } 296 | var storageEntitySet = temp.StorageEntitySet; 297 | var tableName = temp.TableName; 298 | var isRoot = temp.IsRoot; 299 | var baseEdmType = temp.BaseEdmType; 300 | 301 | string schema = (string)storageEntitySet.MetadataProperties["Schema"].Value; 302 | 303 | var entityMap = RegEntity(typeFullName, tableName, schema); 304 | entityMap.IsRoot = isRoot; 305 | entityMap.EdmType = edmItem; 306 | entityMap.ParentEdmType = entityMap.IsRoot ? null : baseEdmType; 307 | 308 | _pks[tableName] = storageEntitySet.ElementType.KeyMembers.Select(x => x.Name).ToArray(); 309 | 310 | entityMap.IsTph = TphData[identity].Discriminators.Count > 0; 311 | 312 | int i = 0; 313 | 314 | var propertiesToMap = GetPropertiesToMap(entityMap, storageEntitySet.ElementType.Properties).ToList(); 315 | var fullName = entityMap.EdmType.FullName; 316 | var entityMembers = TphData[fullName].Properties; 317 | 318 | string prefix = null; 319 | foreach (var edmProperty in propertiesToMap) 320 | { 321 | if (entityMembers.Length <= i) 322 | { 323 | break; 324 | } 325 | 326 | var columnName = edmProperty.Name; 327 | 328 | var edmMember2 = entityMembers.FirstOrDefault(e => { 329 | var metadata = edmProperty.MetadataProperties; 330 | var preferredName = metadata.Any(m => m.Name == "PreferredName") 331 | ? (string) metadata["PreferredName"].Value 332 | : (string) metadata["Name"].Value; 333 | 334 | var edmName = e.Name; 335 | var entityType = e.TypeUsage.EdmType.GetType(); 336 | 337 | if (entityType == typeof (ComplexType) || entityType == typeof(EntityType)) { 338 | return preferredName.StartsWith(edmName + "_"); 339 | } 340 | 341 | var ret = preferredName == edmName; 342 | return ret; 343 | }); 344 | 345 | if (edmMember2 == null) { 346 | i++; 347 | continue; 348 | } 349 | 350 | // check if is complex type 351 | if (edmMember2.TypeUsage.EdmType.GetType() == typeof(ComplexType)) 352 | { 353 | prefix = edmMember2.Name; 354 | } 355 | 356 | string propName; 357 | if (prefix != null && columnName.StartsWith(prefix + "_")) 358 | { 359 | propName = columnName.Replace('_', '.'); 360 | } 361 | else 362 | { 363 | propName = edmMember2.Name; 364 | ++i; 365 | } 366 | 367 | var propInfo = entityMap.Type.GetProperty(propName, '.'); 368 | if (propInfo == null) 369 | { 370 | // entity does not contain such property 371 | continue; 372 | } 373 | 374 | ActuallyAddAProperty(entityMap, edmProperty, propName, columnName, edmMember2); 375 | } 376 | 377 | // create navigation properties 378 | foreach (var navigationProperty in TphData[identity].NavProperties) 379 | { 380 | var associationType = navigationProperty.RelationshipType as AssociationType; 381 | if (associationType == null || associationType.ReferentialConstraints.Count == 0) 382 | { 383 | continue; 384 | } 385 | 386 | var to = associationType.ReferentialConstraints[0].ToProperties[0]; 387 | 388 | var propertyMap = entityMap.Properties.Cast() 389 | .FirstOrDefault(x => x.EdmMember == to); 390 | 391 | if (propertyMap != null) 392 | { 393 | propertyMap.NavigationPropertyName = navigationProperty.Name; 394 | 395 | if (entityMap.Prop(navigationProperty.Name) == null) 396 | { 397 | var navigationPropertyMap = entityMap.MapProperty(navigationProperty.Name, propertyMap.ColumnName); 398 | navigationPropertyMap.IsNavigationProperty = true; 399 | navigationPropertyMap.ForeignKeyPropertyName = propertyMap.PropertyName; 400 | navigationPropertyMap.ForeignKey = propertyMap; 401 | 402 | propertyMap.NavigationProperty = navigationPropertyMap; 403 | } 404 | } 405 | } 406 | 407 | // create discriminators 408 | foreach (var discriminator in TphData[identity].Discriminators) 409 | { 410 | var propertyMap = entityMap.MapDiscriminator(discriminator.Key, discriminator.Value); 411 | entityMap.AddDiscriminator(propertyMap); 412 | } 413 | 414 | return entityMap; 415 | } 416 | 417 | /// 418 | /// Gets properties that are ment for given type. 419 | /// TPH columns are ordered by hierarchy and type name. 420 | /// First columns are from base class. Derived types, which name starts with 'A', columns are before type, which name starts with 'B' etc. 421 | /// So the logic is to include all properties inherited from base types and exclude all already bound properties from siblings. 422 | /// 423 | /// 424 | /// 425 | /// 426 | private IEnumerable GetPropertiesToMap(EntityMap entityMap, IEnumerable properties) 427 | { 428 | if (entityMap.IsRoot) 429 | { 430 | return properties; 431 | } 432 | 433 | var parentEdmType = entityMap.ParentEdmType; 434 | 435 | var include = new List(); 436 | var exclude = new List(); 437 | 438 | while (true) 439 | { 440 | var parent = _entityMaps.Values.FirstOrDefault(x => x.EdmType == parentEdmType); 441 | if (parent == null) 442 | { 443 | break; 444 | } 445 | 446 | include.AddRange(parent.Properties.Cast().Select(x => x.EdmProperty)); 447 | 448 | exclude.AddRange(_entityMaps.Values.Where(x => x.ParentEdmType == parentEdmType) 449 | .SelectMany(x => x.Properties) 450 | .Cast() 451 | .Select(x => x.EdmProperty)); 452 | 453 | parentEdmType = parent.ParentEdmType; 454 | } 455 | 456 | return properties.Where(edmProperty => include.Contains(edmProperty) || !exclude.Contains(edmProperty)).ToList(); 457 | } 458 | 459 | 460 | private void ActuallyAddAProperty(EntityMap entityMap, EdmProperty edmProperty, string propName, string columnName, 461 | EdmMember edmMember) 462 | { 463 | PropertyMap propertyMap; 464 | try 465 | { 466 | propertyMap = entityMap.MapProperty(propName, columnName); 467 | propertyMap.EdmProperty = edmProperty; 468 | propertyMap.EdmMember = edmMember; 469 | propertyMap.IsNavigationProperty = edmMember is NavigationProperty; 470 | propertyMap.IsFk = propertyMap.IsNavigationProperty || _fks.ContainsKey(edmMember); 471 | if (propertyMap.IsFk && _fks.ContainsKey(edmMember)) 472 | { 473 | propertyMap.FkTargetEdmMember = _fks[edmMember]; 474 | entityMap.AddFk(propertyMap); 475 | } 476 | } 477 | catch (Exception ex) 478 | { 479 | var errorMessage = string.Format("Failed to map propName {0} to column {1} on table {2} ({3})", 480 | propName, 481 | columnName, 482 | entityMap.TableName, 483 | entityMap.TypeFullName); 484 | 485 | throw new Exception(errorMessage, ex); 486 | } 487 | 488 | if (_pks[entityMap.TableName].Contains(columnName)) 489 | { 490 | propertyMap.IsPk = true; 491 | entityMap.AddPk(propertyMap); 492 | } 493 | 494 | foreach (var facet in edmProperty.TypeUsage.Facets) 495 | { 496 | //System.Data.Entity.Core.Common.DbProviderManifest.ScaleFacetName 497 | switch (facet.Name) 498 | { 499 | case "SRID": 500 | try 501 | { 502 | propertyMap.SRID = (int?)facet.Value; 503 | } 504 | catch 505 | { 506 | // nothing to do 507 | } 508 | break; 509 | case "IsStrict": 510 | propertyMap.IsStrict = facet.Value != null && (bool)facet.Value; 511 | break; 512 | case "Unicode": 513 | propertyMap.Unicode = facet.Value != null && (bool)facet.Value; 514 | break; 515 | case "FixedLength": 516 | propertyMap.FixedLength = facet.Value != null && (bool)facet.Value; 517 | break; 518 | case "Precision": 519 | propertyMap.Precision = (byte)facet.Value; 520 | break; 521 | case "Scale": 522 | propertyMap.Scale = (byte)facet.Value; 523 | break; 524 | case "Nullable": 525 | propertyMap.IsRequired = !(bool)facet.Value; 526 | break; 527 | case "DefaultValue": 528 | propertyMap.DefaultValue = facet.Value; 529 | break; 530 | case "StoreGeneratedPattern": 531 | propertyMap.IsIdentity = (StoreGeneratedPattern)facet.Value == StoreGeneratedPattern.Identity; 532 | propertyMap.Computed = (StoreGeneratedPattern)facet.Value == StoreGeneratedPattern.Computed; 533 | break; 534 | case "MaxLength": 535 | if (facet.Value == null) 536 | { 537 | propertyMap.MaxLength = int.MaxValue; 538 | } 539 | else 540 | { 541 | int result; 542 | var val = facet.Value.ToString(); 543 | if (!Int32.TryParse(val, out result)) 544 | { 545 | if (val == "Max") 546 | { 547 | propertyMap.MaxLength = int.MaxValue; 548 | } 549 | } 550 | propertyMap.MaxLength = result; 551 | } 552 | break; 553 | } 554 | } 555 | } 556 | 557 | /// 558 | /// 559 | /// 560 | public void BindForeignKeys() 561 | { 562 | var fks = _entityMaps.Values.SelectMany(x => x.Fks); 563 | var pks = _entityMaps.Values.SelectMany(x => x.Pks); 564 | 565 | // can't use ToDictionary, because tph tables share mappings 566 | var pkDict = new Dictionary(); 567 | foreach (PropertyMap columnMapping in pks) 568 | { 569 | pkDict[columnMapping.EdmMember] = columnMapping; 570 | } 571 | 572 | foreach (PropertyMap fkCol in fks) 573 | { 574 | fkCol.FkTargetColumn = pkDict[fkCol.FkTargetEdmMember]; 575 | } 576 | } 577 | } 578 | } -------------------------------------------------------------------------------- /src/EntityFramework.Metadata.Test/DbFirst/TestModel.Context.tt: -------------------------------------------------------------------------------- 1 | <#@ template language="C#" debug="false" hostspecific="true"#> 2 | <#@ include file="EF6.Utility.CS.ttinclude"#><#@ 3 | output extension=".cs"#><# 4 | 5 | const string inputFile = @"TestModel.edmx"; 6 | var textTransform = DynamicTextTransformation.Create(this); 7 | var code = new CodeGenerationTools(this); 8 | var ef = new MetadataTools(this); 9 | var typeMapper = new TypeMapper(code, ef, textTransform.Errors); 10 | var loader = new EdmMetadataLoader(textTransform.Host, textTransform.Errors); 11 | var itemCollection = loader.CreateEdmItemCollection(inputFile); 12 | var modelNamespace = loader.GetModelNamespace(inputFile); 13 | var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); 14 | 15 | var container = itemCollection.OfType().FirstOrDefault(); 16 | if (container == null) 17 | { 18 | return string.Empty; 19 | } 20 | #> 21 | //------------------------------------------------------------------------------ 22 | // 23 | // <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#> 24 | // 25 | // <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#> 26 | // <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#> 27 | // 28 | //------------------------------------------------------------------------------ 29 | 30 | <# 31 | 32 | var codeNamespace = code.VsNamespaceSuggestion(); 33 | if (!String.IsNullOrEmpty(codeNamespace)) 34 | { 35 | #> 36 | namespace <#=code.EscapeNamespace(codeNamespace)#> 37 | { 38 | <# 39 | PushIndent(" "); 40 | } 41 | 42 | #> 43 | using System; 44 | using System.Data.Entity; 45 | using System.Data.Entity.Infrastructure; 46 | <# 47 | if (container.FunctionImports.Any()) 48 | { 49 | #> 50 | using System.Data.Entity.Core.Objects; 51 | using System.Linq; 52 | <# 53 | } 54 | #> 55 | 56 | <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext 57 | { 58 | public <#=code.Escape(container)#>() 59 | : base("name=<#=container.Name#>") 60 | { 61 | <# 62 | if (!loader.IsLazyLoadingEnabled(container)) 63 | { 64 | #> 65 | this.Configuration.LazyLoadingEnabled = false; 66 | <# 67 | } 68 | 69 | foreach (var entitySet in container.BaseEntitySets.OfType()) 70 | { 71 | // Note: the DbSet members are defined below such that the getter and 72 | // setter always have the same accessibility as the DbSet definition 73 | if (Accessibility.ForReadOnlyProperty(entitySet) != "public") 74 | { 75 | #> 76 | <#=codeStringGenerator.DbSetInitializer(entitySet)#> 77 | <# 78 | } 79 | } 80 | #> 81 | } 82 | 83 | protected override void OnModelCreating(DbModelBuilder modelBuilder) 84 | { 85 | throw new UnintentionalCodeFirstException(); 86 | } 87 | 88 | <# 89 | foreach (var entitySet in container.BaseEntitySets.OfType()) 90 | { 91 | #> 92 | <#=codeStringGenerator.DbSet(entitySet)#> 93 | <# 94 | } 95 | 96 | foreach (var edmFunction in container.FunctionImports) 97 | { 98 | WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: false); 99 | } 100 | #> 101 | } 102 | <# 103 | 104 | if (!String.IsNullOrEmpty(codeNamespace)) 105 | { 106 | PopIndent(); 107 | #> 108 | } 109 | <# 110 | } 111 | #> 112 | <#+ 113 | 114 | private void WriteFunctionImport(TypeMapper typeMapper, CodeStringGenerator codeStringGenerator, EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) 115 | { 116 | if (typeMapper.IsComposable(edmFunction)) 117 | { 118 | #> 119 | 120 | [DbFunction("<#=edmFunction.NamespaceName#>", "<#=edmFunction.Name#>")] 121 | <#=codeStringGenerator.ComposableFunctionMethod(edmFunction, modelNamespace)#> 122 | { 123 | <#+ 124 | codeStringGenerator.WriteFunctionParameters(edmFunction, WriteFunctionParameter); 125 | #> 126 | <#=codeStringGenerator.ComposableCreateQuery(edmFunction, modelNamespace)#> 127 | } 128 | <#+ 129 | } 130 | else 131 | { 132 | #> 133 | 134 | <#=codeStringGenerator.FunctionMethod(edmFunction, modelNamespace, includeMergeOption)#> 135 | { 136 | <#+ 137 | codeStringGenerator.WriteFunctionParameters(edmFunction, WriteFunctionParameter); 138 | #> 139 | <#=codeStringGenerator.ExecuteFunction(edmFunction, modelNamespace, includeMergeOption)#> 140 | } 141 | <#+ 142 | if (typeMapper.GenerateMergeOptionFunction(edmFunction, includeMergeOption)) 143 | { 144 | WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: true); 145 | } 146 | } 147 | } 148 | 149 | public void WriteFunctionParameter(string name, string isNotNull, string notNullInit, string nullInit) 150 | { 151 | #> 152 | var <#=name#> = <#=isNotNull#> ? 153 | <#=notNullInit#> : 154 | <#=nullInit#>; 155 | 156 | <#+ 157 | } 158 | 159 | public const string TemplateId = "CSharp_DbContext_Context_EF6"; 160 | 161 | public class CodeStringGenerator 162 | { 163 | private readonly CodeGenerationTools _code; 164 | private readonly TypeMapper _typeMapper; 165 | private readonly MetadataTools _ef; 166 | 167 | public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef) 168 | { 169 | ArgumentNotNull(code, "code"); 170 | ArgumentNotNull(typeMapper, "typeMapper"); 171 | ArgumentNotNull(ef, "ef"); 172 | 173 | _code = code; 174 | _typeMapper = typeMapper; 175 | _ef = ef; 176 | } 177 | 178 | public string Property(EdmProperty edmProperty) 179 | { 180 | return string.Format( 181 | CultureInfo.InvariantCulture, 182 | "{0} {1} {2} {{ {3}get; {4}set; }}", 183 | Accessibility.ForProperty(edmProperty), 184 | _typeMapper.GetTypeName(edmProperty.TypeUsage), 185 | _code.Escape(edmProperty), 186 | _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), 187 | _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); 188 | } 189 | 190 | public string NavigationProperty(NavigationProperty navProp) 191 | { 192 | var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType()); 193 | return string.Format( 194 | CultureInfo.InvariantCulture, 195 | "{0} {1} {2} {{ {3}get; {4}set; }}", 196 | AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)), 197 | navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, 198 | _code.Escape(navProp), 199 | _code.SpaceAfter(Accessibility.ForGetter(navProp)), 200 | _code.SpaceAfter(Accessibility.ForSetter(navProp))); 201 | } 202 | 203 | public string AccessibilityAndVirtual(string accessibility) 204 | { 205 | return accessibility + (accessibility != "private" ? " virtual" : ""); 206 | } 207 | 208 | public string EntityClassOpening(EntityType entity) 209 | { 210 | return string.Format( 211 | CultureInfo.InvariantCulture, 212 | "{0} {1}partial class {2}{3}", 213 | Accessibility.ForType(entity), 214 | _code.SpaceAfter(_code.AbstractOption(entity)), 215 | _code.Escape(entity), 216 | _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); 217 | } 218 | 219 | public string EnumOpening(SimpleType enumType) 220 | { 221 | return string.Format( 222 | CultureInfo.InvariantCulture, 223 | "{0} enum {1} : {2}", 224 | Accessibility.ForType(enumType), 225 | _code.Escape(enumType), 226 | _code.Escape(_typeMapper.UnderlyingClrType(enumType))); 227 | } 228 | 229 | public void WriteFunctionParameters(EdmFunction edmFunction, Action writeParameter) 230 | { 231 | var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); 232 | foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable)) 233 | { 234 | var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null"; 235 | var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")"; 236 | var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + TypeMapper.FixNamespaces(parameter.RawClrTypeName) + "))"; 237 | writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit); 238 | } 239 | } 240 | 241 | public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace) 242 | { 243 | var parameters = _typeMapper.GetParameters(edmFunction); 244 | 245 | return string.Format( 246 | CultureInfo.InvariantCulture, 247 | "{0} IQueryable<{1}> {2}({3})", 248 | AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), 249 | _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), 250 | _code.Escape(edmFunction), 251 | string.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray())); 252 | } 253 | 254 | public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace) 255 | { 256 | var parameters = _typeMapper.GetParameters(edmFunction); 257 | 258 | return string.Format( 259 | CultureInfo.InvariantCulture, 260 | "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});", 261 | _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), 262 | edmFunction.NamespaceName, 263 | edmFunction.Name, 264 | string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()), 265 | _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()))); 266 | } 267 | 268 | public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) 269 | { 270 | var parameters = _typeMapper.GetParameters(edmFunction); 271 | var returnType = _typeMapper.GetReturnType(edmFunction); 272 | 273 | var paramList = String.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray()); 274 | if (includeMergeOption) 275 | { 276 | paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption"; 277 | } 278 | 279 | return string.Format( 280 | CultureInfo.InvariantCulture, 281 | "{0} {1} {2}({3})", 282 | AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), 283 | returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", 284 | _code.Escape(edmFunction), 285 | paramList); 286 | } 287 | 288 | public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) 289 | { 290 | var parameters = _typeMapper.GetParameters(edmFunction); 291 | var returnType = _typeMapper.GetReturnType(edmFunction); 292 | 293 | var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())); 294 | if (includeMergeOption) 295 | { 296 | callParams = ", mergeOption" + callParams; 297 | } 298 | 299 | return string.Format( 300 | CultureInfo.InvariantCulture, 301 | "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});", 302 | returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", 303 | edmFunction.Name, 304 | callParams); 305 | } 306 | 307 | public string DbSet(EntitySet entitySet) 308 | { 309 | return string.Format( 310 | CultureInfo.InvariantCulture, 311 | "{0} virtual DbSet<{1}> {2} {{ get; set; }}", 312 | Accessibility.ForReadOnlyProperty(entitySet), 313 | _typeMapper.GetTypeName(entitySet.ElementType), 314 | _code.Escape(entitySet)); 315 | } 316 | 317 | public string DbSetInitializer(EntitySet entitySet) 318 | { 319 | return string.Format( 320 | CultureInfo.InvariantCulture, 321 | "{0} = Set<{1}>();", 322 | _code.Escape(entitySet), 323 | _typeMapper.GetTypeName(entitySet.ElementType)); 324 | } 325 | 326 | public string UsingDirectives(bool inHeader, bool includeCollections = true) 327 | { 328 | return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) 329 | ? string.Format( 330 | CultureInfo.InvariantCulture, 331 | "{0}using System;{1}" + 332 | "{2}", 333 | inHeader ? Environment.NewLine : "", 334 | includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "", 335 | inHeader ? "" : Environment.NewLine) 336 | : ""; 337 | } 338 | } 339 | 340 | public class TypeMapper 341 | { 342 | private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; 343 | 344 | private readonly System.Collections.IList _errors; 345 | private readonly CodeGenerationTools _code; 346 | private readonly MetadataTools _ef; 347 | 348 | public static string FixNamespaces(string typeName) 349 | { 350 | return typeName.Replace("System.Data.Spatial.", "System.Data.Entity.Spatial."); 351 | } 352 | 353 | public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors) 354 | { 355 | ArgumentNotNull(code, "code"); 356 | ArgumentNotNull(ef, "ef"); 357 | ArgumentNotNull(errors, "errors"); 358 | 359 | _code = code; 360 | _ef = ef; 361 | _errors = errors; 362 | } 363 | 364 | public string GetTypeName(TypeUsage typeUsage) 365 | { 366 | return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); 367 | } 368 | 369 | public string GetTypeName(EdmType edmType) 370 | { 371 | return GetTypeName(edmType, isNullable: null, modelNamespace: null); 372 | } 373 | 374 | public string GetTypeName(TypeUsage typeUsage, string modelNamespace) 375 | { 376 | return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); 377 | } 378 | 379 | public string GetTypeName(EdmType edmType, string modelNamespace) 380 | { 381 | return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace); 382 | } 383 | 384 | public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace) 385 | { 386 | if (edmType == null) 387 | { 388 | return null; 389 | } 390 | 391 | var collectionType = edmType as CollectionType; 392 | if (collectionType != null) 393 | { 394 | return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace)); 395 | } 396 | 397 | var typeName = _code.Escape(edmType.MetadataProperties 398 | .Where(p => p.Name == ExternalTypeNameAttributeName) 399 | .Select(p => (string)p.Value) 400 | .FirstOrDefault()) 401 | ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ? 402 | _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) : 403 | _code.Escape(edmType)); 404 | 405 | if (edmType is StructuralType) 406 | { 407 | return typeName; 408 | } 409 | 410 | if (edmType is SimpleType) 411 | { 412 | var clrType = UnderlyingClrType(edmType); 413 | if (!IsEnumType(edmType)) 414 | { 415 | typeName = _code.Escape(clrType); 416 | } 417 | 418 | typeName = FixNamespaces(typeName); 419 | 420 | return clrType.IsValueType && isNullable == true ? 421 | String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) : 422 | typeName; 423 | } 424 | 425 | throw new ArgumentException("edmType"); 426 | } 427 | 428 | public Type UnderlyingClrType(EdmType edmType) 429 | { 430 | ArgumentNotNull(edmType, "edmType"); 431 | 432 | var primitiveType = edmType as PrimitiveType; 433 | if (primitiveType != null) 434 | { 435 | return primitiveType.ClrEquivalentType; 436 | } 437 | 438 | if (IsEnumType(edmType)) 439 | { 440 | return GetEnumUnderlyingType(edmType).ClrEquivalentType; 441 | } 442 | 443 | return typeof(object); 444 | } 445 | 446 | public object GetEnumMemberValue(MetadataItem enumMember) 447 | { 448 | ArgumentNotNull(enumMember, "enumMember"); 449 | 450 | var valueProperty = enumMember.GetType().GetProperty("Value"); 451 | return valueProperty == null ? null : valueProperty.GetValue(enumMember, null); 452 | } 453 | 454 | public string GetEnumMemberName(MetadataItem enumMember) 455 | { 456 | ArgumentNotNull(enumMember, "enumMember"); 457 | 458 | var nameProperty = enumMember.GetType().GetProperty("Name"); 459 | return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null); 460 | } 461 | 462 | public System.Collections.IEnumerable GetEnumMembers(EdmType enumType) 463 | { 464 | ArgumentNotNull(enumType, "enumType"); 465 | 466 | var membersProperty = enumType.GetType().GetProperty("Members"); 467 | return membersProperty != null 468 | ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null) 469 | : Enumerable.Empty(); 470 | } 471 | 472 | public bool EnumIsFlags(EdmType enumType) 473 | { 474 | ArgumentNotNull(enumType, "enumType"); 475 | 476 | var isFlagsProperty = enumType.GetType().GetProperty("IsFlags"); 477 | return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null); 478 | } 479 | 480 | public bool IsEnumType(GlobalItem edmType) 481 | { 482 | ArgumentNotNull(edmType, "edmType"); 483 | 484 | return edmType.GetType().Name == "EnumType"; 485 | } 486 | 487 | public PrimitiveType GetEnumUnderlyingType(EdmType enumType) 488 | { 489 | ArgumentNotNull(enumType, "enumType"); 490 | 491 | return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null); 492 | } 493 | 494 | public string CreateLiteral(object value) 495 | { 496 | if (value == null || value.GetType() != typeof(TimeSpan)) 497 | { 498 | return _code.CreateLiteral(value); 499 | } 500 | 501 | return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks); 502 | } 503 | 504 | public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable types, string sourceFile) 505 | { 506 | ArgumentNotNull(types, "types"); 507 | ArgumentNotNull(sourceFile, "sourceFile"); 508 | 509 | var hash = new HashSet(StringComparer.InvariantCultureIgnoreCase); 510 | if (types.Any(item => !hash.Add(item))) 511 | { 512 | _errors.Add( 513 | new CompilerError(sourceFile, -1, -1, "6023", 514 | String.Format(CultureInfo.CurrentCulture, CodeGenerationTools.GetResourceString("Template_CaseInsensitiveTypeConflict")))); 515 | return false; 516 | } 517 | return true; 518 | } 519 | 520 | public IEnumerable GetEnumItemsToGenerate(IEnumerable itemCollection) 521 | { 522 | return GetItemsToGenerate(itemCollection) 523 | .Where(e => IsEnumType(e)); 524 | } 525 | 526 | public IEnumerable GetItemsToGenerate(IEnumerable itemCollection) where T: EdmType 527 | { 528 | return itemCollection 529 | .OfType() 530 | .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) 531 | .OrderBy(i => i.Name); 532 | } 533 | 534 | public IEnumerable GetAllGlobalItems(IEnumerable itemCollection) 535 | { 536 | return itemCollection 537 | .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i)) 538 | .Select(g => GetGlobalItemName(g)); 539 | } 540 | 541 | public string GetGlobalItemName(GlobalItem item) 542 | { 543 | if (item is EdmType) 544 | { 545 | return ((EdmType)item).Name; 546 | } 547 | else 548 | { 549 | return ((EntityContainer)item).Name; 550 | } 551 | } 552 | 553 | public IEnumerable GetSimpleProperties(EntityType type) 554 | { 555 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); 556 | } 557 | 558 | public IEnumerable GetSimpleProperties(ComplexType type) 559 | { 560 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); 561 | } 562 | 563 | public IEnumerable GetComplexProperties(EntityType type) 564 | { 565 | return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); 566 | } 567 | 568 | public IEnumerable GetComplexProperties(ComplexType type) 569 | { 570 | return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); 571 | } 572 | 573 | public IEnumerable GetPropertiesWithDefaultValues(EntityType type) 574 | { 575 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); 576 | } 577 | 578 | public IEnumerable GetPropertiesWithDefaultValues(ComplexType type) 579 | { 580 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); 581 | } 582 | 583 | public IEnumerable GetNavigationProperties(EntityType type) 584 | { 585 | return type.NavigationProperties.Where(np => np.DeclaringType == type); 586 | } 587 | 588 | public IEnumerable GetCollectionNavigationProperties(EntityType type) 589 | { 590 | return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many); 591 | } 592 | 593 | public FunctionParameter GetReturnParameter(EdmFunction edmFunction) 594 | { 595 | ArgumentNotNull(edmFunction, "edmFunction"); 596 | 597 | var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters"); 598 | return returnParamsProperty == null 599 | ? edmFunction.ReturnParameter 600 | : ((IEnumerable)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault(); 601 | } 602 | 603 | public bool IsComposable(EdmFunction edmFunction) 604 | { 605 | ArgumentNotNull(edmFunction, "edmFunction"); 606 | 607 | var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute"); 608 | return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null); 609 | } 610 | 611 | public IEnumerable GetParameters(EdmFunction edmFunction) 612 | { 613 | return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); 614 | } 615 | 616 | public TypeUsage GetReturnType(EdmFunction edmFunction) 617 | { 618 | var returnParam = GetReturnParameter(edmFunction); 619 | return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage); 620 | } 621 | 622 | public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption) 623 | { 624 | var returnType = GetReturnType(edmFunction); 625 | return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType; 626 | } 627 | } 628 | 629 | public static void ArgumentNotNull(T arg, string name) where T : class 630 | { 631 | if (arg == null) 632 | { 633 | throw new ArgumentNullException(name); 634 | } 635 | } 636 | #> --------------------------------------------------------------------------------