EF Core optional ValueObject as identity - entity-framework-core

I am using value objects as identities and would like to know how to best deal with nulls in EF core.
For example if I have an employee with an optional title (mr, mrs, etc):
public class Employee
: Entity,
IAggregateRoot
{
public EmployeeTitleId TitleId { get; private set; }
public EmployeeFirstName FirstName { get; private set; }
public EmployeeSurname Surname { get; private set; }
...
}
I could check for nulls everywhere in my code e.g.
if (employee.TitleId == null) ...
or I could use a default e.g.
if (employee.TitleId.Equals(EmployeeTitleId.None)) ...
with the EmployeeTitleId implemented as follows:
public class EmployeeTitleId
: Value<EmployeeTitleId>
{
public static readonly EmployeeTitleId None = new EmployeeTitleId();
protected EmployeeTitleId() { }
public EmployeeTitleId(Guid value)
{
if (value == default)
throw new ArgumentNullException(nameof(value), "Employee title id cannot be empty");
Value = value;
}
public Guid Value { get; internal set; }
public static implicit operator Guid(EmployeeTitleId self) => self.Value;
public static implicit operator EmployeeTitleId(string value)
=> new EmployeeTitleId(Guid.Parse(value));
public override string ToString() => Value.ToString();
}
I would prefer the second approach as it seems cleaner but I don't know if this is overkill. It would also require a bit more setup on the entity framework side e.g.:
builder.OwnsOne(_ => _.TitleId).Property(_ => _.Value)
.HasColumnName("TitleId").HasConversion(v => v == default ? (Guid?) null : v, v => v ?? EmployeeTitleId.None);
Does this seem like a viable approach or should I just stick to checking for null? If so, is there a convention I could use in the entity type configurations so that I don't have to manually set each HasConversion?

There is another way to filter the results globally. You can configure this in the OnModelCreating like:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// code omitted for brevity
modelBuilder.Entity<Employee>().HasQueryFilter(p => p.TitleId == null);
}

Related

Is it possible to add foreign key between owned entities in EF Core 6

I am trying to separate my contexts using DDD. I have two domains, Instruments and Advertisements with its aggregate roots (the example is hypothetical). Instrument AR owns many InstrumentPictures and I would like to have that information in the Advertisement domain as well via proxy entity.
To ensure good database integrity it would be better to create foreign key from AdvertisementPicture.Guid to InstrumentPicture.Guid but as far as I know this can be done only through HasOne/HasMany model configuration.
Am I using the owner relationship wrong?
(Note: I do not want to configure the FK with custom sql migration.)
Instrument AR:
public class Instrument
{
protected Instrument()
{
}
public Instrument(string name, IEnumerable<InstrumentPicture> pictures)
{
Name = name;
_instrumentPictures.AddRange(pictures);
}
protected List<InstrumentPicture> _instrumentPictures = new List<InstrumentPicture>();
public IReadOnlyCollection<InstrumentPicture> InstrumentPictures
=> _instrumentPictures.AsReadOnly();
public Guid Guid { get; private set; }
public string Name { get; private set; }
}
InstrumentPicture owned collection:
public class InstrumentPicture
{
protected InstrumentPicture()
{
}
public InstrumentPicture(Guid guid, string url)
{
Guid = guid;
Url = url;
}
public Guid Guid { get; set; }
public string Url { get; set; }
public DateTime Created { get; set; }
}
Advertisiment AR
public class Advertisement
{
protected Advertisement()
{
}
public Advertisement(Guid instrumentGuid, string name, IEnumerable<AdvertisementPicture> pictures)
{
InstrumentGuid = instrumentGuid;
Name = name;
_advertisementPictures.AddRange(pictures);
}
protected List<AdvertisementPicture> _advertisementPictures = new List<AdvertisementPicture>();
public IReadOnlyCollection<AdvertisementPicture> AdvertisementPictures
=> _advertisementPictures.AsReadOnly();
public Guid Guid { get; private set; }
public Guid InstrumentGuid { get; private set; }
public string Name { get; private set; }
}
AdvertisementPicture proxy
public class AdvertisementPicture
{
protected AdvertisementPicture()
{
}
public AdvertisementPicture(Guid guid, string url)
{
Guid = guid;
Url = url;
}
public Guid Guid { get; set; }
public string Url { get; set; }
}
Model configuration:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Instrument>()
.HasKey(e => e.Guid);
modelBuilder.Entity<Instrument>()
.OwnsMany(e => e.InstrumentPictures, pic =>
{
pic.HasKey(e => e.Guid);
});
modelBuilder.Entity<Advertisement>()
.HasKey(e => e.Guid);
modelBuilder.Entity<Advertisement>()
.HasOne<Instrument>()
.WithMany()
.HasForeignKey(e => e.InstrumentGuid);
modelBuilder.Entity<Advertisement>()
.OwnsMany(e => e.AdvertisementPictures, pic =>
{
pic.HasKey(e => e.Guid);
// How can I add a foreign key to original InstrumentPicture for database integrity?
});
}
I've been struggling with this for hours and finding lots of answers on SO saying this isn't possible. Turns out this is possible using EntityFrameworkCore so I'll post what I've found on my Top Google Search for this problem.
As soon as you add a foreign key you will find the migration tool attempting to create the table in the second DBContext (unless you add ModelBuilder.Ignore<>() which will either do nothing or ignore your foreign key depending on your order of operations).
You can however do something like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IdentityUser>()
.ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}
This will allow you to reference tables in other DBContext's but exclude any changes to them from the one you're working in. This is outlined in the MS documentation here.
If you have used Fluent API you may still need to apply those configurations in the referencing DB Context. This is easily achieved if you have used the IEntityTypeConfiguration<T> implementation by an additional call to ModelBuilder.ApplyConfigurationsFromAssembly(typeof(T).Assembly);.
In such a use case as above you may find yourself excluding a lot of different entities from your DB context. If you have these defined in their own library like I have to follow a DDD pattern you may find an extension method useful to exclude all of them at once:
public static class ExcludeEntitiesInAssemblyFromMigrationsExtension
{
public static void ExcludeEntitiesInAssemblyFromMigrations(this ModelBuilder builder, Assembly assembly)
{
var assemblyTypes = assembly.GetExportedTypes().Where(t => t.IsClass && !t.IsAbstract);
foreach (var assemblyType in assemblyTypes)
{
var entityBuilder = builder.Entity(assemblyType);
var entityTablename = entityBuilder.Metadata.GetTableName();
if (entityTablename != null)
{
entityBuilder.ToTable(entityTablename, t => t.ExcludeFromMigrations());
}
}
}
}

Using DTO with OData in .NetCore 2.1

I am writing a test OData Rest API with an InMemoryDatabase.
I would like to use DTO(s) to hide the SQL model and adjust a few fields (geographic positions and so on).
However, when I use ProjectTo<...> method from AutoMapper, GET request to the API return an empty collection instead of the actual result list.
Do you have any idea about what I am doing wrong ?
Here is the controller :
namespace offers_api.Controllers
{
public class OffersController : ODataController
{
private readonly OfferContext _context;
private IMapper _mapper;
public OffersController(OfferContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
[EnableQuery]
public IActionResult Get()
{
IQueryable<Offer> res = _context.Offers.ProjectTo<Offer>(_mapper.ConfigurationProvider); // <-- works without ProjectTo !
return Ok(res);
}
}
}
The automapper declaration :
namespace offers_api.Entities
{
public class Mapping : Profile
{
public Mapping()
{
//CreateMap<CategoryEntity, string>().ConvertUsing(cat => cat.Name ?? string.Empty);
CreateMap<LocationEntity, Location>()
.ForMember(x => x.longitude, opt => opt.MapFrom(o => 0))
.ForMember(x => x.latitude, opt => opt.MapFrom(o => 0))
.ReverseMap();
CreateMap<OfferEntity, Offer>()
.ForMember(x => x.Category, opt => opt.MapFrom(o => o.Category.Name))
.ReverseMap()
.ForMember(x => x.Category, opt => opt.MapFrom(o => new CategoryEntity { Name = o.Category }));
CreateMap<OfferPictureEntity, OfferPicture>().ReverseMap();
CreateMap<UserEntity, User>().ReverseMap();
}
}
}
The EDM model :
private static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Offer>("Offers");
return builder.GetEdmModel();
}
I found the solution.
In fact, automapper loaded more data than OData's default behaviour.
The relation between an offer and it's author was described by a non nullable foreing key. I didn't insert any author in the DB, but OData tried to load a user and saw it was missing in the USER table, so it discarded the Offer result.
Solution : make the foreign key nullable.
namespace offers_api.Entities
{
public class OfferEntity
{
[Key]
public long Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public long AuthorId { get; set; } // <== Bug here : add long? to resolve it...
public virtual UserEntity Author { get; set; }
}
}

Entity Framework 6 Code first Default value

is there "elegant" way to give specific property a default value ?
Maybe by DataAnnotations, something like :
[DefaultValue("true")]
public bool Active { get; set; }
Thank you.
You can do it by manually edit code first migration:
public override void Up()
{
AddColumn("dbo.Events", "Active", c => c.Boolean(nullable: false, defaultValue: true));
}
It's been a while, but leaving a note for others.
I achieved what is needed with an attribute and I decorated my model class fields with that attribute as I want.
[SqlDefaultValue(DefaultValue = "getutcdate()")]
public DateTime CreatedDateUtc { get; set; }
Got the help of these 2 articles:
EF on CodePlex
Andy Mehalick blog
What I did:
Define Attribute
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class SqlDefaultValueAttribute : Attribute
{
public string DefaultValue { get; set; }
}
In the "OnModelCreating" of the context
modelBuilder.Conventions.Add( new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.Single().DefaultValue));
In the custom SqlGenerator
private void SetAnnotatedColumn(ColumnModel col)
{
AnnotationValues values;
if (col.Annotations.TryGetValue("SqlDefaultValue", out values))
{
col.DefaultValueSql = (string)values.NewValue;
}
}
Then in the Migration Configuration constructor, register the custom SQL generator.
SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());
The above answers really helped, but only delivered part of the solution.
The major issue is that as soon as you remove the Default value attribute, the constraint on the column in database won't be removed. So previous default value will still stay in the database.
Here is a full solution to the problem, including removal of SQL constraints on attribute removal.
I am also re-using .NET Framework's native DefaultValue attribute.
Usage
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[DefaultValue("getutcdate()")]
public DateTime CreatedOn { get; set; }
For this to work you need to update IdentityModels.cs and Configuration.cs files
IdentityModels.cs file
Add/update this method in your ApplicationDbContext class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var convention = new AttributeToColumnAnnotationConvention<DefaultValueAttribute, string>("SqlDefaultValue", (p, attributes) => attributes.SingleOrDefault().Value.ToString());
modelBuilder.Conventions.Add(convention);
}
Configuration.cs file
Update your Configuration class constructor by registering custom Sql generator, like this:
internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
public Configuration()
{
// DefaultValue Sql Generator
SetSqlGenerator("System.Data.SqlClient", new DefaultValueSqlServerMigrationSqlGenerator());
}
}
Next, add custom Sql generator class (you can add it to the Configuration.cs file or a separate file)
internal class DefaultValueSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
private int dropConstraintCount;
protected override void Generate(AddColumnOperation addColumnOperation)
{
SetAnnotatedColumn(addColumnOperation.Column, addColumnOperation.Table);
base.Generate(addColumnOperation);
}
protected override void Generate(AlterColumnOperation alterColumnOperation)
{
SetAnnotatedColumn(alterColumnOperation.Column, alterColumnOperation.Table);
base.Generate(alterColumnOperation);
}
protected override void Generate(CreateTableOperation createTableOperation)
{
SetAnnotatedColumns(createTableOperation.Columns, createTableOperation.Name);
base.Generate(createTableOperation);
}
protected override void Generate(AlterTableOperation alterTableOperation)
{
SetAnnotatedColumns(alterTableOperation.Columns, alterTableOperation.Name);
base.Generate(alterTableOperation);
}
private void SetAnnotatedColumn(ColumnModel column, string tableName)
{
if (column.Annotations.TryGetValue("SqlDefaultValue", out var values))
{
if (values.NewValue == null)
{
column.DefaultValueSql = null;
using var writer = Writer();
// Drop Constraint
writer.WriteLine(GetSqlDropConstraintQuery(tableName, column.Name));
Statement(writer);
}
else
{
column.DefaultValueSql = (string)values.NewValue;
}
}
}
private void SetAnnotatedColumns(IEnumerable<ColumnModel> columns, string tableName)
{
foreach (var column in columns)
{
SetAnnotatedColumn(column, tableName);
}
}
private string GetSqlDropConstraintQuery(string tableName, string columnName)
{
var tableNameSplitByDot = tableName.Split('.');
var tableSchema = tableNameSplitByDot[0];
var tablePureName = tableNameSplitByDot[1];
var str = $#"DECLARE #var{dropConstraintCount} nvarchar(128)
SELECT #var{dropConstraintCount} = name
FROM sys.default_constraints
WHERE parent_object_id = object_id(N'{tableSchema}.[{tablePureName}]')
AND col_name(parent_object_id, parent_column_id) = '{columnName}';
IF #var{dropConstraintCount} IS NOT NULL
EXECUTE('ALTER TABLE {tableSchema}.[{tablePureName}] DROP CONSTRAINT [' + #var{dropConstraintCount} + ']')";
dropConstraintCount++;
return str;
}
}
Your model properties don't have to be 'auto properties' Even though that is easier. And the DefaultValue attribute is really only informative metadata
The answer accepted here is one alternative to the constructor approach.
public class Track
{
private const int DEFAULT_LENGTH = 400;
private int _length = DEFAULT_LENGTH;
[DefaultValue(DEFAULT_LENGTH)]
public int LengthInMeters {
get { return _length; }
set { _length = value; }
}
}
vs.
public class Track
{
public Track()
{
LengthInMeters = 400;
}
public int LengthInMeters { get; set; }
}
This will only work for applications creating and consuming data using this specific class. Usually this isn't a problem if data access code is centralized. To update the value across all applications you need to configure the datasource to set a default value. Devi's answer shows how it can be done using migrations, sql, or whatever language your data source speaks.
What I did, I initialized values in the constructor of the entity
Note: DefaultValue attributes won't set the values of your properties automatically, you have to do it yourself
I admit that my approach escapes the whole "Code First" concept. But if you have the ability to just change the default value in the table itself... it's much simpler than the lengths that you have to go through above... I'm just too lazy to do all that work!
It almost seems as if the posters original idea would work:
[DefaultValue(true)]
public bool IsAdmin { get; set; }
I thought they just made the mistake of adding quotes... but alas no such intuitiveness. The other suggestions were just too much for me (granted I have the privileges needed to go into the table and make the changes... where not every developer will in every situation). In the end I just did it the old fashioned way. I set the default value in the SQL Server table... I mean really, enough already! NOTE: I further tested doing an add-migration and update-database and the changes stuck.
After #SedatKapanoglu comment, I am adding all my approach that works, because he was right, just using the fluent API does not work.
1- Create custom code generator and override Generate for a ColumnModel.
public class ExtendedMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
protected override void Generate(ColumnModel column, IndentedTextWriter writer, bool emitName = false)
{
if (column.Annotations.Keys.Contains("Default"))
{
var value = Convert.ChangeType(column.Annotations["Default"].NewValue, column.ClrDefaultValue.GetType());
column.DefaultValue = value;
}
base.Generate(column, writer, emitName);
}
}
2- Assign the new code generator:
public sealed class Configuration : DbMigrationsConfiguration<Data.Context.EfSqlDbContext>
{
public Configuration()
{
CodeGenerator = new ExtendedMigrationCodeGenerator();
AutomaticMigrationsEnabled = false;
}
}
3- Use fluent api to created the Annotation:
public static void Configure(DbModelBuilder builder){
builder.Entity<Company>().Property(c => c.Status).HasColumnAnnotation("Default", 0);
}
It's simple! Just annotate with required.
[Required]
public bool MyField { get; set; }
the resultant migration will be:
migrationBuilder.AddColumn<bool>(
name: "MyField",
table: "MyTable",
nullable: false,
defaultValue: false);
If you want true, change the defaultValue to true in the migration before updating the database
In .NET Core 3.1 you can do the following in the model class:
public bool? Active { get; set; }
In the DbContext OnModelCreating you add the default value.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Foundation>()
.Property(b => b.Active)
.HasDefaultValueSql("1");
base.OnModelCreating(modelBuilder);
}
Resulting in the following in the database
Note:
If you don't have nullable (bool?) for you property you will get the following warning
The 'bool' property 'Active' on entity type 'Foundation' is configured with a database-generated default. This default will always be used for inserts when the property has the value 'false', since this is the CLR default for the 'bool' type. Consider using the nullable 'bool?' type instead so that the default will only be used for inserts when the property value is 'null'.
I found that just using Auto-Property Initializer on entity property is enough to get the job done.
For example:
public class Thing {
public bool IsBigThing{ get; set; } = false;
}
using System.ComponentModel;
[DefaultValue(true)]
public bool Active { get; set; }
In EF core released 27th June 2016 you can use fluent API for setting default value. Go to ApplicationDbContext class, find/create the method name OnModelCreating and add the following fluent API.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<YourTableName>()
.Property(b => b.Active)
.HasDefaultValue(true);
}
Just Overload the default constructor of Model class and pass any relevant parameter which you may or may not use. By this you can easily supply default values for attributes. Below is an example.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Aim.Data.Domain
{
[MetadataType(typeof(LoginModel))]
public partial class Login
{
public Login(bool status)
{
this.CreatedDate = DateTime.Now;
this.ModifiedDate = DateTime.Now;
this.Culture = "EN-US";
this.IsDefaultPassword = status;
this.IsActive = status;
this.LoginLogs = new HashSet<LoginLog>();
this.LoginLogHistories = new HashSet<LoginLogHistory>();
}
}
public class LoginModel
{
[Key]
[ScaffoldColumn(false)]
public int Id { get; set; }
[Required]
public string LoginCode { get; set; }
[Required]
public string Password { get; set; }
public string LastPassword { get; set; }
public int UserGroupId { get; set; }
public int FalseAttempt { get; set; }
public bool IsLocked { get; set; }
public int CreatedBy { get; set; }
public System.DateTime CreatedDate { get; set; }
public Nullable<int> ModifiedBy { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
public string Culture { get; set; }
public virtual ICollection<LoginLog> LoginLogs { get; set; }
public virtual ICollection<LoginLogHistory> LoginLogHistories { get; set; }
}
}
Even from .NET Core 1.0, It is possible to set default values when you are using the code first approach. See the following code snippet.
using System.ComponentModel;
[DefaultValue(true)]
public bool Active { get; set; }
Read for more: Microsoft official docs
Lets consider you have a class name named Products and you have a IsActive field. just you need a create constructor :
Public class Products
{
public Products()
{
IsActive = true;
}
public string Field1 { get; set; }
public string Field2 { get; set; }
public bool IsActive { get; set; }
}
Then your IsActive default value is True!
Edite :
if you want to do this with SQL use this command :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.IsActive)
.HasDefaultValueSql("true");
}
The Entity Framework Core Fluent API HasDefaultValue method is used to specify the default value for a database column mapped to a property. The value must be a constant.
public class Contact
{
public int ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public bool IsActive { get; set; }
public DateTime DateCreated { get; set; }
}
public clas SampleContext : DbContext
{
public DbSet<Contact> Contacts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Context>()
.Propery(p => p.IsActive)
.HasDefaultValue(true);
}
}
Or
like it!
You can also specify a SQL fragment that is used to calculate the default value:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Created)
.HasDefaultValueSql("getdate()");
}
Hmm... I do DB first, and in that case, this is actually a lot easier. EF6 right? Just open your model, right click on the column you want to set a default for, choose properties, and you will see a "DefaultValue" field. Just fill that out and save. It will set up the code for you.
Your mileage may vary on code first though, I haven't worked with that.
The problem with a lot of other solutions, is that while they may work initially, as soon as you rebuild the model, it will throw out any custom code you inserted into the machine-generated file.
This method works by adding an extra property to the edmx file:
<EntityType Name="Thingy">
<Property Name="Iteration" Type="Int32" Nullable="false" **DefaultValue="1"** />
And by adding the necessary code to the constructor:
public Thingy()
{
this.Iteration = 1;
Set the default value for the column in table in MSSQL Server, and in class code add attribute, like this:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
for the same property.

Change name of Identity Column for all Entities

I am in the process of creating a domain model and would like to have a "BaseEntity" class with an "Id" property (and some other audit tracking stuff). The Id property is the primary key and each Entity in my Domain Model will inherit from the BaseEntity class. Pretty straightforward stuff.....
public class BaseEntity
{
[Key]
public int Id { get; set; }
public DateTime LastUpdate { get; set; }
public string LastUpdateBy { get; set; }
}
public class Location : BaseEntity
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
Using the example above, I would like to map the "Id" field to a "LocationId" column. I understand that I can use the modelBuilder to do this for each entity explicitly by doing something like this:
modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");
But I would like to do this for every Entity in my domain model and it would be ugly.
I tried the following bit of reflection but did not have any luck. For whatever reason, the compiler "cannot resolve symbol type":
foreach (var type in GetTypesInNamespace(Assembly.Load("Domain.Model"),"Domain.Model"))
{
modelBuilder.Entity<type>().Property(x=>x.Id).....
}
Is there a way to define a convention to override the default PrimaryKey convention to map my "Id" property to a "ClassNameId" property in the database? I am using Entity Framework 6.
You should take a look at Custom Code First Conventions. You need EF6 for it to work, but it looks like you're already using it.
Just to give you an overview, take a look at the following convention I've used to convert PascalCase names to underscore names. It includes a convention for id properties... It also includes an optional table name prefix.
public class UnderscoreNamingConvention : IConfigurationConvention<PropertyInfo, PrimitivePropertyConfiguration>,
IConfigurationConvention<Type, ModelConfiguration>
{
public UnderscoreNamingConvention()
{
IdFieldName = "Id";
}
public string TableNamePrefix { get; set; }
public string IdFieldName { get; set; }
public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
var columnName = propertyInfo.Name;
if (propertyInfo.Name == IdFieldName)
columnName = propertyInfo.ReflectedType.Name + IdFieldName;
configuration().ColumnName = ToUnderscore(columnName);
}
public void Apply(Type type, Func<ModelConfiguration> configuration)
{
var entityTypeConfiguration = configuration().Entity(type);
if (entityTypeConfiguration.IsTableNameConfigured) return;
var tableName = ToUnderscore(type.Name);
if (!string.IsNullOrEmpty(TableNamePrefix))
{
tableName = string.Format("{0}_{1}", TableNamePrefix, tableName);
}
entityTypeConfiguration.ToTable(tableName);
}
public static string ToUnderscore(string value)
{
return Regex.Replace(value, "(\\B[A-Z])", "_$1").ToLowerInvariant();
}
}
You use it like this
modelBuilder.Conventions.Add(new UnderscoreNamingConvention { TableNamePrefix = "app" });
EDIT: In your case, the Apply method should be something like this:
public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
if (propertyInfo.Name == "Id")
{
configuration().ColumnName = propertyInfo.ReflectedType.Name + "Id";
}
}
Try this out in your DbContext class;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<int>()
.Where(p => p.Name.Equals("Id"))
.Configure(c => c.HasColumnName(c.ClrPropertyInfo.ReflectedType.Name + "Id"));
}
int is the CLR Type of my Primary Key fields. I want to refer to all keys in code as Id but DBA's require keys to be Id with Table entity name prefix. Above gives me exactly what I want in my created database.
Entity Framework 6.x is required.
In Entity Framework 6 Code First:
modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");
and update-database...
Change in model
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long id { get; set; }
to:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long role_id { get; set; }
Then remove this:
//modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");
A start to the Dynamic approach if NOT using custom conventions
modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");
You can do this using reflection on the context. Pseudo Code as explanation:
Reflect Context to get a list of POCO names
For each POCO in a dbcontext.
Map Property Id -> string PocoName+Id
Here are the extensions I use for this type of solution.
// DBSet Types is the Generic Types POCO name used for a DBSet
public static List<string> GetModelTypes(this DbContext context) {
var propList = context.GetType().GetProperties();
return GetDbSetTypes(propList);
}
// DBSet Types POCO types as IEnumerable List
public static IEnumerable<Type> GetDbSetPropertyList<T>() where T : DbContext {
return typeof (T).GetProperties().Where(p => p.PropertyType.GetTypeInfo()
.Name.StartsWith("DbSet"))
.Select(propertyInfo => propertyInfo.PropertyType.GetGenericArguments()[0]).ToList();
}
private static List<string> GetDbSetTypes(IEnumerable<PropertyInfo> propList) {
var modelTypeNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.PropertyType.GenericTypeArguments[0].Name)
.ToList();
return modelTypeNames;
}
private static List<string> GetDbSetNames(IEnumerable<PropertyInfo> propList) {
var modelNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.Name)
.ToList();
return modelNames;
}
However, you will still need to employee dynamic lambda to finish.
Continue that topic here: Dynamic lambda example with EF scenario
EDIT:
Add link to another question that address the common BAse Config class approach
Abstract domain model base class when using EntityTypeConfiguration<T>
Piggybacking on #Monty0018 's answer but this just need to be updated a little if, like me, you're using Entity Framework 7 and/or SQLite.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
try
{
_builder = modelBuilder;
var typeName = typeof(T).Name;
_builder
.Entity(typeof(T))
.Property<int>("Id")
.ForSqliteHasColumnName(typeName + "Id");
}
catch (Exception e)
{
throw e;
}
}

How to map a string property to a binary column in the database?

I have a class for a user entity. One of the properties is the user's password (a hash, actually). I made it a string (streamlined code):
public class User
{
public virtual int Id { get; set; }
public virtual string Password { get; set; }
}
There's also a Fluent NHibernate mapping (streamlined code):
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("users");
Id(x => x.Id).GeneratedBy.Sequence("users_id_seq");
Map(x => x.Password); // what do I put here???
}
}
The database column is of bytea data type on PostgreSQL. The above mapping doesn't work, because the property is string (text). What should I do?
you can Make Password a public property, which is only used to reference to the underlying private property HashedPassword.
something like so:
protected virtual byte[] /*or whatever the type is*/ HashedPassword {get; set;}
public virtual string Password
get
{
return (string)(HashedPassword); //or however you want to cast it to string...
}
set
//...
you can then tell fluent nHib to ignore your Password property.
Here is the final solution which works both ways (read & write), what I tried earlier didn't work properly. Hope this will help somebody.
public class User
{
private byte[] _password;
public virtual int Id { get; private set; }
public virtual string Password
{
get { return System.Text.Encoding.Unicode.GetString(_password); }
set { _password = System.Text.Encoding.Unicode.GetBytes(value); }
}
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Table("users");
Id(x => x.Id).GeneratedBy.Sequence("users_id_seq");
Map(x => x.Password)
.Access.LowerCaseField(Prefix.Underscore)
.CustomType<byte[]>();
}
}