I generally create a domain model and use data annotations/explicit foreign key relationships in that domain model to define how the database is created. To me, this makes certain rules (like max length or required) clear and in one place.
I was going through a code review and one part of the feedback was to not do either of those things. They recommended I do everything in the configuration for each model using Fluent API/shadow properties instead so that all database generation code is in one place and that the model stays clean and only concerns itself with the domain alone.
As an example, I updated my code to something similar to this:
public class Blog
{
private HashSet<Comment> comments;
public IReadOnlyCollection<Comment> Comments => comments;
public string Title {get; private set;}
public string Content {get; private set;}
public Core.Constants.Blog.Categories Category {get; private set;}
public Blog(string title, string content, Core.Constants.Blog.Categories category)
{
Id = Guid.NewGuid().ToString("D");
comments = new HashSet<Comment>();
Title = title;
Content = content;
Category = category;
}
}
public class Comment{
private string Id {get; private set;}
public string Identifier { get; private set; }
public string Description { get; private set; }
public Comment(string identifier, string description)
{
Id = Guid.NewGuid().ToString("D");
Identifier = identifier;
Description = description;
}
}
and added fluent configuration code with:
internal class BlogConfiguration : IEntityTypeConfiguration<Domain.Blog>
{
public void Configure(EntityTypeBuilder<Domain.Blog> builder)
{
builder.ToTable("Blog");
builder.HasKey("Id").HasName("PK_Blog");
builder.Property(res => res.Title)
.IsRequired();
builder.Property(res => res.Content)
.IsRequired();
builder.Metadata
.FindNavigation(nameof(Domain.Blog.Comments))
.SetPropertyAccessMode(PropertyAccessMode.Field);
builder.Property(blog => blog.Category)
.HasConversion(value =>
value.ToString(),
convertedValue => Enum.Parse<Core.Constants.Blog.Categories>(convertedValue));
}
}
internal class CommentConfig : IEntityTypeConfiguration<Domain.Comment>
{
public void Configure(EntityTypeBuilder<Domain.Comment> builder) {
builder.ToTable("Comment");
builder.HasKey("Id").HasName("PK_Comment");
builder.Property(asset => asset.Identifier)
.HasMaxLength(25)
.IsRequired();
builder.Property(asset => asset.Description)
.HasMaxLength(250);
builder.HasIndex(asset => asset.Identifier).IsUnique();
}
}
My questions are:
In my actual project, I need a navigation property filled from "Comment" back to "Blog". Is it bad to add an explicitly navigation property on Comment that points back to Blog that is ignored in the fluent configuration?
Is using Data Annotations on the domain model really that bad of a practice?
Related
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());
}
}
}
}
For the life of me, I cannot get this to work properly. I have a relatively simple domain model that has a couple of navigation properties that I want to fill out via eager loading.
To keep my domain model pure, I have opted to use shadow properties as foreign keys, so they are not accessible by the client code.
This is the domain model:
public class CourseType : Entity
{
protected CourseType() { }
public CourseType(string name, CoachGroup coachGroup, bool active)
{
Name = name;
CoachGroup = coachGroup;
Active = active;
}
public string Name { get; private set; }
private int? _coachGroupId;
private int? CoachGroupId => _coachGroupId;
public virtual CoachGroup CoachGroup { get; private set; }
public int? AgeLimit { get; private set; }
public bool Active { get; private set; }
public bool ShowInSearchForm { get; private set; }
private List<Course> _accessGivingCourses = new List<Course>();
public virtual IReadOnlyList<Course> AccessGivingCourses => _accessGivingCourses?.ToList();
}
This is how I wire the configuration up:
public class CourseTypeConfiguration : IEntityTypeConfiguration<CourseType>
{
public void Configure(EntityTypeBuilder<CourseType> builder)
{
builder.ToTable("CourseTypes", "Courses");
builder.HasKey(p => p.Id);
builder.Property(p => p.Id).HasColumnName("CourseTypeId");
builder.Property(p => p.Name).HasColumnName("CourseTypeName");
builder.Property(p => p.Active).HasColumnName("IsActive");
builder.Property(p => p.ShowInSearchForm).HasColumnName("ShowInSearchForm");
builder.Property(p => p.AgeLimit).HasColumnName("AgeLimit");
builder.Property<int?>("CoachGroupId").HasField("_coachGroupId");
builder.HasOne(p => p.CoachGroup).WithOne().HasForeignKey<CourseType>("CoachGroupId").OnDelete(DeleteBehavior.Restrict);
builder.HasMany(p => p.AccessGivingCourses).WithMany("AccessGivingCourses")
.UsingEntity<Dictionary<string, object>>("CourseTypesAccessGivingCourses",
j => j.HasOne<Course>().WithMany().HasForeignKey("CourseId"),
j => j.HasOne<CourseType>().WithMany().HasForeignKey("CourseTypeId"),
j => j.ToTable("CourseTypesAccessGivingCourses")
);
builder.HasIndex("CoachGroupId").IsUnique(false);
}
}
This is how I extract the data via my repository class:
public override async Task<IEnumerable<CourseType>> GetAll()
{
try
{
return await Context.CourseTypes.Include(i => i.CoachGroup).Include(i => i.AccessGivingCourses).ToListAsync();
}
catch (Exception e)
{
Logger.LogCritical(e, $"Could not retrieve list of course type entities");
throw;
}
}
It ALMOST works, except for the fact that when I add or update entities, the CoachGroup link randomly gets lost for some updates. For others, it works just fine. It's like Entity Framework Core OR the database randomly loses track of it. Which is odd, because when I look in the database table, the foreign keys in the table are all there like they're supposed to!?
Does anyone have any idea what the hell I am doing wrong? Or if this is the correct approach to this problem at all? All I want to do is to load related data, but it's getting to the point where it's becoming rocket science to merely link a couple of optional relationships together...
I have the following Entity class definition:
[Table("Users")]
public class WebUser
{
public virtual int Id { get; set; }
public virtual ICollection<Client> Clients { get; set; }
// more properties...
}
Notice that table name is different than the class name. I also have a ClientUsers table which is a many-to-many mapping for clients and users. Problem is, when I try to access the webUser.Clients property I get the following exception:
"Invalid object name 'dbo.ClientWebUsers'."
Looks like Entity Framework is trying to guess the name of the third table, but it apparently was not smart enough to take into account the table attribute that I have there. How can I tell EF that it is ClientUsers and not ClientWebUsers? Also what rule does it follow to know which table name comes first and which one comes second in the new table name? I think it's not alphabetical order.
I'm using EF 5.0. Thanks!
From the looks of things you're using Code First, so I'll answer accordingly. If this is incorrect, please let me know.
I believe the convention being used to determine the name of the many-to-many table is determined by the order in which they occur as DbSet properties in your SomeContext : DbContext class.
As for forcing EntityFramework to name your table whatever you like, you can use the Fluent API in the OnModelCreating method of your SomeContext : DbContext class as follows:
public class DatabaseContext : DbContext
{
public DatabaseContext()
: base("SomeDB")
{
}
public DbSet<WebUser> Users { get; set; }
public DbSet<Client> Clients { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<WebUser>().HasMany(c => c.Clients)
.WithMany(p => p.WebUsers).Map(
m =>
{
m.MapLeftKey("ClientId");
m.MapRightKey("UserId");
m.ToTable("ClientUsers");
});
}
}
This assumes your classes are something like the following:
[Table("Users")]
public class WebUser
{
public virtual int Id { get; set; }
public virtual ICollection<Client> Clients { get; set; }
// more properties...
}
public class Client
{
public int Id { get; set; }
public ICollection<WebUser> WebUsers { get; set; }
// more properties
}
Finally, here's an integration test (NUnit) demonstrating the functionality working. You may need to drop your database before running it as Code First should want to update/migrate/recreate it.
[TestFixture]
public class Test
{
[Test]
public void UseDB()
{
var db = new DatabaseContext();
db.Users.Add(new WebUser { Clients = new List<Client> { new Client() } });
db.SaveChanges();
var webUser = db.Users.First();
var client = webUser.Clients.FirstOrDefault();
Assert.NotNull(client);
}
}
Edit: Link to relevant documentation for the Fluent API
Rowan's answer (adding here for reference):
Here is the information on how to configure a many-to-many table (including specifying the table name). The code you are after is something like:
modelBuilder.Entity<WebUser>()
.HasMany(u => u.Clients)
.WithMany(c => c.WebUsers)
.Map(m => m.ToTable("ClientUsers");
~Rowan
When serializing a MongoDB document to a POCO is there any way to make properties map case insensitive? For example I'd like this document:
{
"id": "1"
"foo": "bar"
}
to map to this class:
public MyObj
{
public int Id {get; set;}
public string Foo {get; set;}
}
To do that I think you will have 2 options.
The first would be to write out a class map manually
BsonClassMap.RegisterClassMap<MyClass>(cm => {
cm.AutoMap();
cm.GetMemberMap(c => c.Foo).SetElementName("foo");
});
The second would be to decorate your class with the following attributes
public class MyObj
{
[BsonElement("id")]
public int Id { get; set; }
[BsonElement("foo")]
public string Foo { get; set; }
}
The CSharp driver team have a good tutorial on serialization on the following link
http://docs.mongodb.org/ecosystem/tutorial/serialize-documents-with-the-csharp-driver/
Update
I have just tried the following and this works for me, obviously I'm sure this is a much more simplified version of your code but taking a guess at how it might look.
I have registered the two class maps separately and added the BsonKnownType to the base class.
[BsonKnownTypes(typeof(GeoJSONObject))]
public class Point
{
public string Coordinates { get; set; }
}
public class GeoJSONObject : Point
{
public string Type { get; set; }
}
static void Main(string[] args)
{
var cn = new MongoConnectionStringBuilder("server=localhost;database=MyTestDB;");
var settings = MongoClientSettings.FromConnectionStringBuilder(cn);
var client = new MongoClient(settings);
BsonClassMap.RegisterClassMap<Point>(cm =>
{
cm.AutoMap();
cm.GetMemberMap(c => c.Coordinates).SetElementName("coordinates");
});
BsonClassMap.RegisterClassMap<GeoJSONObject>(cm =>
{
cm.AutoMap();
cm.GetMemberMap(c => c.Type).SetElementName("type");
});
var result = client.GetServer()
.GetDatabase("MyTestDB")
.GetCollection("MyCol")
.Find(Query.EQ("type", BsonValue.Create("xxxx")));
}
I see that it is old question, but people still may search it. At least I found it while was asking the same question.
The CamelCaseElementNameConvention can be used to apply this globally.
var pack = new ConventionPack();
pack.Add(new CamelCaseElementNameConvention());
ConventionRegistry.Register("Camel case convention", pack, t => true);
Documentation: https://mongodb.github.io/mongo-csharp-driver/2.14/reference/bson/mapping/conventions/
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;
}
}