I'm new to EF Core 2 and am trying to create Entity properties that are not null with a default value of a blank space. I'm using the fluent API. The end result is a default data binding of N'('''')' on the SqlServer side. I'm fairly certain this is a bug, but is there a known workaround?
I've tried string manipulation to account for the extra apostrophes as well as a variable. However, when I look at the migration script, it's correct so it seems it's happening between the update-database and server communication process and is out of my control.
public class EfficacyDBContext : DbContext
{
public virtual DbSet<Person> Person { get; protected set; }
public virtual DbSet<PersonType> PersonType { get; protected set; }
public EfficacyDBContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new PersonEntityTypeConfiguration());
modelBuilder.ApplyConfiguration(new PersonTypeEntityTypeConfiguration());
}
}
public class PersonEntityTypeConfiguration : IEntityTypeConfiguration<Person>
{
public void Configure(EntityTypeBuilder<Person> builder)
{
builder.ToTable("Person.Person");
builder.HasKey(x => x.Id);
builder.Property(x => x.Id).ValueGeneratedOnAdd();
builder.Property(x => x.FirstName).HasMaxLength(50)
.HasDefaultValue("('')").IsRequired();
}
}
On the SqlServer side I expect the field to default to a blank value when nothing is entered. However, because of the skewy data binding, I get a default value of ('')
You are using HasDefaultValue method incorrectly. It expects a value while your are passing SQL fragment. The later is supported, but by a different method - HasDefaultValueSql.
So either use
.HasDefaultValue("")
or
.HasDefaultValueSql("('')")
Reference: EF Core documentation - Default Values
Related
I have the following Entity Framework (v6.1.3) mapping:
public class FileStoreDocumentEntityMapping : EntityTypeConfiguration<FileStoreDocumentEntity>
{
public FileStoreDocumentEntityMapping()
{
Property(x => x.FileStoreDownloadUrl)
.HasColumnName("FileStoreDetailsUrl")
.HasColumnType("varchar")
.HasMaxLength(1000);
Property(x => x.FileStoreVersion)
.HasColumnName("FileStoreVersion")
.HasColumnType("varchar")
.HasMaxLength(100);
}
}
Both columns are of data type = "varchar" in my database and sizes are correct:
However, some of my unit tests are failing with this error:
"System.InvalidOperationException: Sequence contains no matching element"
I read in a related post that you can get the error above if you pass an invalid type to the HasColumnType method but in my case "varchar" should be valid.
Any ideas what could be wrong?
Here is how those properties are defined in my entity:
public virtual string FileStoreVersion
{
get;
set;
}
public virtual string FileStoreDetailsUrl
{
get;
set;
}
Here is an example of one test that fails in the first line:
[TestMethod]
public void Delete_ReturnsSuccess()
{
_context.DistributionListSelectionCriteriaDepartmentEntities.Add(_distributionListSelectionCriteriaDepartmentEntity);
_context in this case is of type MemoryEnterprisePaycorCodeFirstContext:
private void InitializeTestObjects()
{
_context = new MemoryEnterprisePaycorCodeFirstContext();
Which inherits from my public class EnterprisePaycorCodeFirstContext : DbContext
That class is where i'm initializing my entity mappings:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new FileStoreDocumentEntityMapping());
Could this be because I'm using Effort MemoryContext vs a Real DB context?
public class MemoryEnterprisePaycorCodeFirstContext : EnterprisePaycorCodeFirstContext
{
public MemoryEnterprisePaycorCodeFirstContext()
: base(DbConnectionFactory.CreateTransient())
{
Database.CreateIfNotExists();
}
I figured what it was. Turns out that I was using Effort library which does not support some Column Types as pointed out by #Balah. I used his technique to bypass the entity mapping configuration when coming from my Memory Context:
Effort (EF Unit Testing) giving errors
I'm currently trying to write to a table which inherits from an abstract base class. When I try to do this I get the following error (The ContactMethod property is the discriminator):
System.Data.SqlClient.SqlException: Invalid column name 'ContactMethod'.
EmailContactDetails.cs:
public class EmailContactDetail : ContactDetail
{
[ApiMember(Description = "The Contact Method")]
public override ContactMethod ContactMethod => ContactMethod.Email;
[ApiMember(Description = "Email Address")]
public string EmailAddress { get; set; }
}
EmailContactDetailConfiguration.cs:
public class EmailContactDetailsConfiguration : IEntityTypeConfiguration<EmailContactDetail>
{
public void Configure(EntityTypeBuilder<EmailContactDetail> builder) => Configure(builder, "dbo");
public void Configure(EntityTypeBuilder<EmailContactDetail> builder, string schema)
{
builder.Property(x => x.EmailAddress).HasColumnName("EmailAddress").HasColumnType("nvarchar(255)");
}
}
ContactDetail.cs:
public abstract class ContactDetail
{
[ApiMember(Description = "The Identifier")]
public Guid Id { get; set; }
[ApiMember(Description = "The Contact Method")]
public virtual ContactMethod ContactMethod { get; set; }
}
ContactDetailConfiguration.cs
public class ContactDetailsConfiguration : IEntityTypeConfiguration<ContactDetail>
{
public void Configure(EntityTypeBuilder<ContactDetail> builder) => Configure(builder, "dbo");
public void Configure(EntityTypeBuilder<ContactDetail> builder, string schema)
{
builder.ToTable("ContactDetails", schema);
// Table per hierarchy. all subclasses share the same db table for performance.
builder.HasDiscriminator(x => x.ContactMethod)
.HasValue<EmailContactDetail>(ContactMethod.Email);
builder.Property(x => x.Id).HasColumnName("Id").IsRequired().HasColumnType("uniqueidentifier").ValueGeneratedOnAdd();
}
}
I've tried hiding the discriminator "ContactMethod" by adding the following to the ContactDetailConfiguration.cs file:
builder.Ignore(x => x.ContactMethod);
Once I've done that I end up with the following error
The entity type 'EmailContactDetail' is part of a hierarchy, but does not have a discriminator property configured.
You shouldn't hide the property configured as TPH discriminator from EF because it is essential for EF Core implementation of the TPH strategy.
The initial error simply indicates that your model and database are out of sync. It's true that by convention EF Core uses string shadow property and column called Discriminator. But the whole purpose of HasDiscriminator fluent API is to allow changing the discriminator property/column type, as well as mapping it to an existing property of your entity model.
Which is the case here. You've told EF Core to use your existing property ContactMethod as discriminator, hence EF Core is looking for column named ContactMethod in the database table. So to resolve the issue, simply update your database from the model (using the usual procedure when model is changed - add new migration, update database etc).
I'm attempting to map a class which looks like below but I'm getting an exception regarding the Facility.Projects property being an interface.
The property Facility.Projects is of an interface type IProject. If it is a navigation property manually configure the relationship for this property by casting it to a mapped entity type, otherwise ignore the property from the model.
public class Facility : BaseData
{
[ForeignKey("ClientId")]
public Client Owner { get; set; }
public List<IProject> Projects { get; protected set; }
public Facility()
{
Initialize();
}
private void Initialize()
{
Projects = new List<IProject>();
}
}
I've mapped other properties via the OnModelCreating method for the DbContext class that Facility is mapped to but I'm not really seeing anything that seems like the right spot to cast this other than HasColumnType which also appears wrong.
public class FacilityRepository : BaseRepository<Facility>, IFacilityRepository
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder
.Entity<Client>()
.Property(f => f.Id)
.ForSqliteHasColumnName("ClientId");
modelBuilder
.Entity<Project>()
.Property(x => x.Id)
.ForSqliteHasColumnName("ProjectId");
// area where I've tried and failed to find a good spot to do some manual casting
modelBuilder
.Entity<Facility>()
.Property(f => f.Projects)
.HasColumnType<Project>(new PropertyBuilder());
}
}
Can anyone point me in the right direction? I've not seen anything in the documentation that might help? Or should I, by rule, not be doing this at all?
To do a manual mapping of a one-to-many relationship (this example is just exposing the children on the parent, not vice versa), you can set this up as follows, and then it's pretty straightforward where the cast should be:
modelBuilder.Entity<ProjectState>()
.HasMany(h => (ICollection<ProjectRoleState>)h.ProjectRoleStates)
.WithOne()
.HasForeignKey(p=>p.ProjectGuid);
I'm developing a code-first database, using Entity Framework 6.
I know I can set [MaxLength(myLen)] on the property of a model.
What I wondered, is if this is possible to do in a filter or a custom attribute, so that all strings take on a default, of say 250, unless specified directly on the property.
Failing this, is there a way to change the default of nvarchar(max)?
Entity Framework introduced Custom Code First Conventions for this in 6.1
modelBuilder.Properties<string>()
.Configure(c => c.HasMaxLength(250));
Conventions operate in a last wins manner and the Fluent API and Data Annotations can be used to override a convention in specific cases
You can do this, which ensures all strings are the maximum length supported by the database provider:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<string>().Configure(p => p.IsMaxLength());
}
Add this method (or modify the existing one) in your DbContext class.
In EF6 you can use a custom code first convention, but you will also need to have a way to specify nvarchar(max) data type to a string property. So, I came up with the following solution.
Also see:
https://msdn.microsoft.com/en-us/data/jj819164#order
/// <summary>
/// Set this attribute to string property to have nvarchar(max) type for db table column.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class TextAttribute : Attribute
{
}
/// <summary>
/// Changes all string properties without System.ComponentModel.DataAnnotations.StringLength or
/// Text attributes to use string length 16 (i.e nvarchar(16) instead of nvarchar(max) by default).
/// Use TextAttribute to a property to have nvarchar(max) data type.
/// </summary>
public class StringLength16Convention : Convention
{
public StringLength16Convention()
{
Properties<string>()
.Where(p => !p.GetCustomAttributes(false).OfType<DatabaseGeneratedAttribute>().Any())
.Configure(p => p.HasMaxLength(16));
Properties()
.Where(p => p.GetCustomAttributes(false).OfType<TextAttribute>().Any())
.Configure(p => p.IsMaxLength());
}
}
public class CoreContext : DbContext, ICoreContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Change string length default behavior.
modelBuilder.Conventions.Add(new StringLength16Convention());
}
}
public class LogMessage
{
[Key]
public Guid Id { get; set; }
[StringLength(25)] // Explicit data length. Result data type is nvarchar(25)
public string Computer { get; set; }
//[StringLength(25)] // Implicit data length. Result data type is nvarchar(16)
public string AgencyName { get; set; }
[Text] // Explicit max data length. Result data type is nvarchar(max)
public string Message { get; set; }
}
In this code ModelBuilder class defines the shape of your entities, the relationships between them, and how they map to the database.
public class WebsiteDBContext : DbContext
{
public WebsiteDBContext(DbContextOptions<WebsiteDBContext> options) : base(options)
{
}
public DbSet<Global> Globals { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
// it should be placed here, otherwise it will rewrite the following settings!
base.OnModelCreating(builder);
builder.Entity<Global>();
builder.Entity<Global>(entity =>
{
entity.Property(global => global.MainTopic).HasMaxLength(150).IsRequired();
entity.Property(global => global.SubTopic).HasMaxLength(300).IsRequired(false);
entity.Property(global => global.Subject).IsRequired(false);
entity.Property(global => global.URL).HasMaxLength(150).IsRequired(false);
});
}
}
How do I make non persisted properties using codefirst EF4?
MS says there is a StoreIgnore Attribute, but I cannot find it.
http://blogs.msdn.com/b/efdesign/archive/2010/03/30/data-annotations-in-the-entity-framework-and-code-first.aspx
Is there a way to set this up using EntityConfiguration?
In EF Code-First CTP5, you can use the [NotMapped] annotation.
using System.ComponentModel.DataAnnotations;
public class Song
{
public int Id { get; set; }
public string Title { get; set; }
[NotMapped]
public int Track { get; set; }
Currently, I know of two ways to do it.
Add the 'dynamic' keyword to the property, which stops the mapper persisting it:
private Gender gender;
public dynamic Gender
{
get { return gender; }
set { gender = value; }
}
Override OnModelCreating in DBContext and remap the whole type, omitting the properties you don't want to persist:
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Person>().MapSingleType(p => new { p.FirstName, ... });
}
Using method 2, if the EF team introduce Ignore, you will be able to easily change the code to:
modelBuilder.Entity<Person>().Property(p => p.IgnoreThis).Ignore();
If you don't want to use Annotations, you can use the Fluent API. Override the OnModelCreating and use DbModelBuilder's Ignore() method. Supposing you have a 'Song' entity:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Song>().Ignore(p => p.PropToIgnore);
}
}
You can also use EntityTypeConfiguration to move configurations to separate classes for better manageability:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new SongConfiguration());
}
}
public class SongConfiguration : EntityTypeConfiguration<Song>
{
public SongConfiguration()
{
Ignore(p => p.PropToIgnore);
}
}
I'm not sure if this is available yet.
On this MSDN page the Ignore Attribute and API are described but below, in the comments, somebody writes on 4 june 2010:
You will be able to ignore properties in the next Code First release,
Add
using System.ComponentModel.DataAnnotations.Schema
to the model class. (Must include "SCHEMA")
Add [NotMapped] data annotation to the field(s) you want to keep from persisting (ie. not save to database).
This will prevent them from being added as a column to the table in the db.
Please note - previous answers may have included these bits, but they did not have the full "using" clause. They merely left off "schema" - under which the NotMapped attribute is defined.