I am using EF Code first with database first approach.
"with Database.SetInitializer(null);"
My table has two columns createddate and amendddate. They are managed by SQL Server using triggers. The idea is that when data entry happens then these columns gets data via triggers.
Now What I want to do is to make this read only from EF Code first point of view. I.e. I want to be able to see the createddate and ameneded dates from my app but I dont want to amend these data.
I have tried using private modifiers on setter but no luck.When I try to add new data to the table it tried to enter DateTime.Max date to the database which throws error from SQL server.
Any idea?
You cannot use private modifiers because EF itself needs to set your properties when it is loading your entity and Code First can only do this when a property has public setter (in contrast to EDMX where private setters are possible (1), (2)).
What you need to do is mark your for CreatedDate with DatabaseGeneratedOption.Identity and your AmendDate with DatabaseGeneratedOption.Computed. That will allow EF to correctly load data from the database, reload data after insert or update so that entity is up to date in your application and at the same time it will not allow you to change the value in the application because the value set in the application will never be passed to the database. From an object oriented perspective it is not a very nice solution but from the functionality perspective it is exactly what you want.
You can do it either with data annotations:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime CreatedDate { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime AmendDate { get; set; }
Or with fluent API in OnModelCreating override in your derived context:
modelBuilder.Entity<YourEntity>()
.Property(e => e.CreatedDate)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<YourEntity>()
.Property(e => e.AmendDate)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
EF core 1.1 or later versions yes you can use read only property in poco classes. What you need to do is using backing-field.
public class Blog
{
private string _validatedUrl;
public int BlogId { get; set; }
public string Url
{
get { return _validatedUrl; }
}
public void SetUrl(string url)
{
using (var client = new HttpClient())
{
var response = client.GetAsync(url).Result;
response.EnsureSuccessStatusCode();
}
_validatedUrl = url;
}
}
class MyContext : DbContext
{
public DbSet Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.HasField("_validatedUrl");
}
}
and fluent api...
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.HasField("_validatedUrl")
.UsePropertyAccessMode(PropertyAccessMode.Field);
Take a look here..
Related
I'm migrating an existing project based on DDD design principles from efcore 2.2 to efcore 3.1. Database setup is based on the series of articles that Julie Lerman wrote a few years ago.
Generally this has been fine but I'm struggle to resolve an issued with owned entities and in particular this error message:
InvalidOperationException: The type 'ApplicationCore.Entities.UserAggregate.Email' cannot be configured as non-owned because an owned entity type with the same name already exists
The two entities are:
public class User
{
public int Id { get; private set; }
public Guid GuidId { get; private set; }
public Email Email {get; private set;}
}
and it's "owned" entity
public class Email
{
public string Address { get; private set; }
}
Formerly in EfCore 2.2 the configuration was:
private static void ConfigureUser(EntityTypeBuilder<User> builder)
{
builder.HasKey(s => s.Id);
builder.Property(s => s.GuidId)
.IsRequired();
builder.OwnsOne(u => u.Email);
}
As far as I understand what I should be doing in efcore3.1 is to update this to be:
private static void ConfigureUser(EntityTypeBuilder<User> builder)
{
builder.HasKey(s => s.Id);
builder.Property(s => s.GuidId)
.IsRequired();
builder.OwnsOne(u => u.Email).WithOwner();
}
As well as this configure method there are several more for the other entities within the OnModelCreating() method
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<ForecastSetup>(ConfigureForecastSetup);
…
builder.Entity<User>(ConfigureUser);
// Remove internal property
foreach (var entityType in builder.Model.GetEntityTypes())
{
builder.Entity(entityType.Name).Ignore("IsDirty");
}
}
The exception will be thrown from the builder.Entity(entityType.Name).Ignore("IsDirty") line.
And that's it. However, this makes zero difference and the same error reappears.
I can't run add-migrations to test if there is something else being setup as the exception is being thrown and I'm unsure as to what will happen if I delete the ContextModelSnapshot…
Thanks #IvanStoev, see the question he links to in the comments.
The config was correct, my problem was occur when trying to remove the Shadow property
// Remove shadow property for entities which are not owned
foreach (var entityType in builder.Model.GetEntityTypes().Where(e => !e.IsOwned()))
{
builder.Entity(entityType.Name).Ignore("IsDirty");
}
Question: Is there a way in Entity Framework Core 3.0 to apply value conversion on backing fields?
Context:
Let's say I have a Blog entity that contains a list of string values representing post ids. I want to avoid the need to have a join entity/table (as described here) so I do a conversion to store the list as a string in the DB. I also want to protect my model from being modified directly through the PostIds property, so I want to use a backing field for that (as described here).
Something like this:
public class Blog
{
public int BlogId { get; set; }
private readonly List<string> _postIds;
public IReadOnlyCollection<string> PostIds => _postIds;
public Blog()
{
_posts = new List<Post>();
}
}
And configuration of the context would look something like that:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Configuring the backing field
modelBuilder.Entity<Blog>()
.Metadata
.FindNavigation(nameof(Blog.PostIds))
.SetPropertyAccessMode(PropertyAccessMode.Field);
// Trying to configure the value conversion, but that doesn't work...
modelBuilder.Entity<Blog>()
.Property(e => e.PostIds)
.HasConversion(v => string.Join(',', v),
v => v.Split(','));
}
Any ideas how this configuration could be achieved with the current version of Entity Framework (3.0)?
You can specified a Unique Key with Fluent Api:
public class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasIndex(u => u.Nickname)
.IsUnique();
}
}
public class User
{
public int UserId { get; set; }
public string Nickname { get; set; }
}
But can you do it with Data Annotations?
Edit
Methods change in EF7 Beta 8:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Index(u => u.Nickname)
.Unique();
}
I'm afraid create an Index using Data Annotation is not still supported in EF 7. Check this link.
I also tried to find some info related with that subject in the last releases and I couldn't find anything.
EF 7 beta 8 release notes
EF 7 RC1 release notes
I found now a post from one of the EF developers (divega) saying this:
In EF7 we support defining indexes using the fluent API but not an
attribute, at least no yet. The IndexAttribute you are possibly
referring to is something we added to the EF 6.x package at some point
but never really became a standard DataAnnotation.
We don't want to copy the original attribute from EF6 as is because
there are a few things in it that we would like to change. Also,
having it in DataAnnotations directly would likely make more sense
than adding it to the EF7 package.
I should mention though that it is highly unlikely that we will add
IndexAttribute in the EF7 RTM timeframe.
Update 1
Apparently this is a feature that will not be added to EF Core, at least for now.
From EF Core documentation:
Indexes can not be configured using Data Annotations.
But you can do it using Fluent Api:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasIndex(b => b.Url)
.HasName("Index_Url");
}
In the absence of built in support, you can use a custom attribute of your own to annotate model properties and apply in OnModelCreating():
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
foreach (var prop in entity.GetProperties())
{
var index = prop.PropertyInfo.GetCustomAttribute<IndexAttribute>();
if (index != null)
{
entity.AddIndex(prop);
}
}
}
}
With a simple marker attribute class:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class IndexAttribute : Attribute
{
}
Then in your model class, just add the attribute to create a secondary index:
public class User
{
public int UserId { get; set; }
[Index]
public string Nickname { get; set; }
}
I already have a database with tables outside EF scope. But I want that the tables which will be used by EF to be created automatically.
public class SessionInfo
{
public Guid Id {get;set;}
public string Name { get; set; }
public DateTime StartsOn { get; set; }
public DateTime EndsOn { get; set; }
public string Notes { get; set; }
}
public class StudentsDbContext:DbContext
{
public StudentsDbContext():base("name=memory")
{
Database.Log = s => this.LogDebug(s);
}
public DbSet<SessionInfo> Sessions { get; set; }
}
This code just throws an exception because the table SessionInfoes doesn't exist.
using (var db = new StudentsDbContext())
{
db.Sessions.Add(new SessionInfo() {Id = Guid.NewGuid(), Name = "bla"});
var st = db.Sessions.FirstOrDefault();
}
What do I need to do so that EF will create the "SessionInfoes" (whatever name, it's not important) table by itself? I was under the impression that Ef will create the tables when the context is first used for a change or a query.
Update
After some digging, it seems that EF and Sqlite don't play very nice together i.e at most you can use EF to do queries but that's it. No table creation, no adding entities.
EF needs additional information in order to do this. You'll have to specify an IDatabaseInitializer first. Take a look at this list and find one that is appropriate for your needs (for example: MigrateDatabaseToLatestVersion, DropCreateDatabaseAlways, DropCreateDatabaseIfModelChanges, etc).
Then create your class:
public class MyDatabaseInitializer : MigrateDatabaseToLatestVersion
<MyDbContext,
MyDatabaseMigrationConfiguration>
Then also create the configuration for the initializer (ugh right?):
public class DatabaseMigrationsConfiguration
: DbMigrationsConfiguration<MyDbContext>
{
public DatabaseMigrationsConfiguration()
{
this.AutomaticMigrationDataLossAllowed = true;
this.AutomaticMigrationsEnabled = true;
}
protected override void Seed(MyDbContext context)
{
// Need data automagically added/update to the DB
// during initialization?
base.Seed(context);
}
}
Then one way to initialize the database is:
var myContext = new MyDbContext(/*connectionString*/);
Database.SetInitializer<MyDbContext>(new MyDatabaseInitializer());
myContext.Database.Initialize(true);
Some people prefer the to use the command line to migrate databases, but I don't want to assume I'll always have access to the database from a command lin.
I want to use .Id in my entity classes for the unique id, but our dba wants [tablename]Id in the database tables. Is there a way that Entity Framework can make this mapping automatically without having to create a new map file for every entity?
As long as I understand you correctly, you have something like:
public class Foo
{
public Int32 ID { get; set; }
// ...
}
public class Bar
{
public Int32 ID { get; set; }
// ...
}
And, without too much effort (or creating multiple entityTypeConfiguration<T> models) you'd like something along the lines of the following outcome:
Current Mapping Desired Mapping
[Foo] [Foo]
ID FooID
... ...
[Bar] [Bar]
ID BarID
... ...
For this, a few methods exist (and depend on which version of EF you're using). With that said, some approachable tactics:
ColumnAttribute
You can visit each entity model and decorate the ID property with the ColumnAttribute. This tells EF that, despite what we named the column, we want something else to be the name within the database. e.g.
public class Foo
{
[Column("FooID")]
public Int32 ID { get; set; }
// ...
}
public class Foo
{
[Column("BarID")]
public Int32 ID { get; set; }
// ...
}
The only problem here is that you're now going to every model and adding the attribute.
OnModelCreating & Fluent Mapping
Another method is to do the mapping but keep it all in one place. The OnModelCreating event is great for this kind of thing.
public class MyDbContext : DbContext
{
public Dbset<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
protected override void OnmodelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Foo>()
.Property(x => x.ID).HasColumnName("FooID");
modelBuilder.Entity<Bar>()
.Property(x => x.ID).HasColumnName("BarID");
}
}
Again, the problem here is that you're creating a configuration for each entity.
Custom Conventions
As of EF6, you can use Custom Conventions which make things easier (Including developing your own convention that would make ID=TableNameID). Unfortunately I don't have the time to write an example, but the docs are pretty enlightening.
According to MSDN , both way should work.
Primary key detection is case insensitive. Recognized naming patterns
are, in order of precedence: 'Id' [type name]Id