ADO.Net - Map table char column to enum - entity-framework

In an existing SQL table a nvarchar column holds enumerative informations.
I now try to use a enum class to map this column using ADO.Net Entity Framework. But somehow this does not seems possible since it requires an integer column.
Does ADO EF not support enums mapped to char columns, or how to realize it allowing linq or lambda syntax on querying?
Error: Given element assignment is invalid.
I am more used to hibernate orm where this is easily possible.

You can use private properties in your model to map your data to whatever property type you want.
// Model
public class Piece
{
// Subclass Piece to add mappings for private properties
public class PieceConfig : EntityTypeConfiguration<Piece>
{
public PieceConfig()
{
Property(b => b.dbtype); // needed for EF to see the private property
}
}
[Column("type", TypeName = "VARCHAR")]
private string dbtype { get; set; }
[NotMapped]
public PIECE type
{
get { return (PIECE)Enum.Parse(typeof(PIECE), dbtype); }
set { dbtype= value.ToString(); }
}
}
Then you just need to add the configuration to your OnModelCreating method
modelBuilder.Configurations.Add(new Piece.PieceConfig());

Related

how to change auto generated "shadow properties" foreign key name for all entities?

i've a model like that
public class Class1 {
public int identifier {get;set;}
}
public class Class2 {
public int identifier {get;set;}
public List<Class1> holders {get;set;}
public List<Class1> users{get;set;}
}
my problem is the generated foreign keys in Class1 name are "Class2_identifier" and "Class2_identifier1" mean while what i want is "Class2_holders_identifier" and "Class2_users_identifier"
the real model is really huge so what i'm looking for is away to override how the names are generated in the "add-migration" step
Not a complete implementation, just a hint: If you are using EntityFramework 6 you can define a custom model convention:
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType>
{
public void Apply(AssociationType association, DbModel model)
{
if (association.IsForeignKey)
{
var constraint = association.Constraint;
// Implement your renaming code.
// The data you need is inside association.Constraint.
}
}
}
And add it to your DbContext.OnModelCreating:
modelBuilder.Conventions.Add(new ForeignKeyNamingConvention());
This answer contains some code that you can reuse (in this case the convention is used to remove underscores in the column names).
Edit: OP included their final solution here:
The problem as mentioned in ef core "it's the same problem in ef6 but with no message" console
There are multiple relationships between 'Class1' and 'Class2' without configured foreign key properties causing EF to create shadow properties on 'Organization' with names dependent on the discovery order.
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType>
{
public void Apply(AssociationType association, DbModel model)
{
if (association.IsForeignKey)
{
var constraint = association.Constraint;
// as i just needed the fk column name to be more clear
// "{entityName}_{propertyName}" which is provided in
// {association.Name}
association.Constraint.ToProperties[0].Name = association.Name;
}
}
}

Changing EF6 source code for conversion of short to bool

What is the feasibility of modifying the mapping code to convert a short of value zero or non-zero to false or true, if the boolean destination property is marked with an attribute in the POCO model?
I mean, this is supposed to be one of the advantages of EF being open sourced, and would be for in house use only.
Any tips on where in the code I would look would be appreciated, but this question is really more general and I'd like to hear anything anyone has to say on this.
With regard to the General comments please.
I dont know to make the EF change, but dealing with similar issues is not an uncommon issue in EF.
Not all standard types are supported by EF.
You can have a helper field in your POCO class.
So one field is the actual DB field, but no used outside of POCO.
The help field is NOTMAPPED or ignored in fluent API.
You access the DB via you helper and execute any required casting.
A simple example. Or the reverse if I got helper and DB field types back to front.
[NotMapped]
public virtual bool IsVisible { set; get; } // Helper Field NOT on DB
public int Test { get { return IsVisible ? 1 : 0; } // on DB, but set and get via helper only.
set { IsVisible = (value != 0); } }
Edit: Power Fluent API
Here is a snippet that outlines how you have code that runs for every mapped poco in a consistent way.
public class MyDbContext : DbContext
// model building, set breakpoint so you know when this is triggered
// it is important this ISNT called everytime, only on model cache.
// in my case that is app pool recycle.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
// use the CONFIG add feature to better organize and allow use of inheritance when mapping
// I will use snippets and statics to keep it simple.
modelBuilder.Configurations.Add(XYZMap.Map()); // POCO map
modelBuilder.Configurations.Add(ABCMAP.Map()); // poco map
modelBuilder.Configurations.Add(XXXMap.MAP()); // poco map
// etc for your POCO set
// Note, no need to declare DBset<xyz> XYZs {get;set;} !!!!
public static class XYZMap {
public static BaseEntityIntConfiguration<PocoXYZ> Map() {
//see return object !
var entity = new BaseEntityLongConfiguration<PocoXYZ>();
//entity.Property()... // map away as usual POCO specifc
///entity.HasRequired()...// property and relationships as required
// do nothing for default
return entity;
}
}
}
// all tables with int key use this base config. do it once never again
public class BaseEntityIntConfiguration<T> : BaseEntityConfiguration<T> where T : BaseObjectInt {
public BaseEntityIntConfiguration(DatabaseGeneratedOption DGO = DatabaseGeneratedOption.Identity) {
// Primary Key
this.HasKey(t => t.Id);
// Properties
//Id is an int allocated by DB
this.Property(t => t.Id).HasDatabaseGeneratedOption(DGO); // default to db generated
// optimistic lock is also added here, Specific to out poco design
this.Property(t => t.RowVersion)
.IsRequired()
.IsFixedLength()
.HasMaxLength(8)
.IsRowVersion();
// any other common mappings/ rules ??
}
}
public class BaseEntityConfiguration<T> : EntityTypeConfiguration<T> where T : BaseObject {
public BaseEntityConfiguration() {
this.ApplyAttributeRules(); // <<<<< Here is where I apply SYSTEM WIDE rules
}
}
public static void ApplyAttributeRules<T>(this EntityTypeConfiguration<T> entity) where T : BaseObject {
// so this will be called for each mapped type
foreach (var propertyInfo in typeof (T).GetProperties()) {
// I use reflection to look for properties that meet certain criteria.
// eg string. I want as NVARCHAR 4000 not NVCAHR max so i can index it.
if (propertyInfo.UnderLyingType().FullName == "System.String") {
SetStringLength(BosTypeTool.StringLengthIndexable, propertyInfo.Name, entity);
continue;
}
SetStringLength(4000, propertyInfo.Name, entity);
}
}
private static void SetStringLength<TModelPoco>(int length, string propertyName,
EntityTypeConfiguration<TModelPoco> entity) where TModelPoco : BaseObject {
var propLambda = DynamicExpression.ParseLambda<TModelPoco, String>(propertyName);
entity.Property(propLambda).HasMaxLength(length);
// dynamic library from Microsoft.... http://msdn.microsoft.com/en-US/vstudio/bb894665.aspx
}
// get underlying type incase it is nullable
public static Type UnderLyingType(this PropertyInfo propertyInfo) {
return Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType;
}

Decoupling Entity Framework from my POCO classes

I'm dynamically creating my DbContext by iterating over any entities that inherit from EntityBase and adding them to my Context:
private void AddEntities(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var entityTypes = assembly.GetTypes()
.Where(x => x.IsSubclassOf(typeof(EntityBase)) && !x.IsAbstract);
foreach (var type in entityTypes)
{
dynamic entityConfiguration = entityMethod.MakeGenericMethod(type).Invoke(modelBuilder, new object[] { });
EntityBase entity = (EntityBase)Activator.CreateInstance(type);
//Add any specific mappings that this class has defined
entity.OnModelCreating(entityConfiguration);
}
}
}
That way, I can have many namespaces but just one generic repository in my base namespace that's used everywhere. Also, in apps that make use of multiple namespaces, the base repository will already be setup to use all the entities in all the loaded namespaces. My problem is, I don't want to make EntityFramework.dll a dependency of every namespace in the company. So I'm calling OnModelCreating and passing the EntityTypeConfiguration to the class so it can add any mappings. This works fine and here's how I can add a mapping to tell the model that my "Description" property comes from a column called "Descriptor":
class Widget... {
public override void OnModelCreating(dynamic entity)
{
System.Linq.Expressions.Expression<Func<Widget, string>> tmp =
x => x.Description;
entity.Property(tmp).HasColumnName("Descriptor");
}
The good thing is, my entity class has no reference to EF, this method is only called once, when the context is created and if we scrap EF and go to something else in the future, my classes won't have all sorts of attributes specific to EF in them.
The problem is, it's super ugly. How can I let the model know about column mappings and keys in a simpler way than creating these Expressions to get properties to map without hard coding references to EF all over my poco classes?
You could define your own Attributes and use these to control the configuration within OnModelCreating(). You should be able to gain (using reflection) all the details you need for column mapping in one linq query a second query for the creation of the key.
public class DatabaseNameAttribute : Attribute
{
private readonly string _name;
public DatabaseNameAttribute(string name)
{
_name = name;
}
public string Name
{
get
{
return _name;
}
}
}
public class KeySequenceAttribute : Attribute
{
private readonly int _sequence;
public KeySequenceAttribute(int sequence)
{
_sequence = sequence;
}
public int Sequence
{
get
{
return _sequence;
}
}
}
[DatabaseName("BlogEntry")]
public class Post
{
[DatabaseName("BlogId")]
[KeySequence(1)]
public int id { get; set; }
[DatabaseName("Description")]
public string text { get; set; }
}

Can I store enums as strings in EF 5?

We have been using EF CF for a while in our solution. Big fans! Up to this point, we've been using a hack to support enums (creating an extra field on the model; ignore the enum durring mapping; and map the extra field to the column in the db that we would have used). Traditionally we have been storing our enums as strings(varchars) in the DB (makes it nice and readable). Now with enum support in EF 5 (Beta 2) it looks like it only supports mapping enums to int columns in the DB....Can we get EF 5 to store our enums as their string representation.
Where "Type" is an enum of type DocumentType
public enum DocumentType
{
POInvoice,
NonPOInvoice,
Any
}
I tried to map it using:
public class WorkflowMap : EntityTypeConfiguration<Model.Workflow.Workflow>
{
public WorkflowMap()
{
ToTable("Workflow", "Workflow");
...
Property(wf => wf.Type).HasColumnType("varchar");
}
}
I thought was going to be the magic bullet but..
That just throws:
Schema specified is not valid. Errors: (571,12) : error 2019: Member
Mapping specified is not valid. The type
'Dodson.Data.DataAccess.EFRepositories.DocumentType[Nullable=False,DefaultValue=]'
of member 'Type' in type
'Dodson.Data.DataAccess.EFRepositories.Workflow' is not compatible
with
'SqlServer.varchar[Nullable=False,DefaultValue=,MaxLength=8000,Unicode=False,FixedLength=False]'
of member 'Type' in type 'CodeFirstDatabaseSchema.Workflow'.
Your thoughts?
This is currently not possible. Enum in EF has same limitations as enums in CLR - they are just named set of integer values. Check this article for confirmation:
The EF enum type definitions live in conceptual layer. Similarly to
CLR enums the EF enums have underlying type which is one of Edm.SByte,
Edm.Byte, Edm.Int16, Edm.Int32 or Edm.Int64 with Edm.Int32 being the
default underlying type if none has been specified.
I posted article and related suggestion about this problem. If you want to see this feature in the future please vote for the suggestion.
I hit this problem a few weeks ago. The best I could come up with is a bit hacky.
I have a Gender enum on the class Person, and I use data annotations to map the string to the database and ignore the enum.
public class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Column("Gender")]
public string GenderString
{
get { return Gender.ToString(); }
private set { Gender = value.ParseEnum<Gender>(); }
}
[NotMapped]
public Gender Gender { get; set; }
}
And the extension method to get the correct enum from the string.
public static class StringExtensions
{
public static T ParseEnum<T>(this string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
}
See this post for full details - http://nodogmablog.bryanhogan.net/2014/11/saving-enums-as-strings-with-entity-framework/

Entity Framework Mapping DateTimeOffset to SQL Server DateTime

Is there a way to map a DateTimeOffset property to a SQL Server datetime column, with the assumption that you can't change either side, meaning that the property and column have to stay those date types?
I know the easiest is to make them match but want to know if there's a way to work around this. I was looking into custom mappings but it seemed like I had to map all of the columns myself and not just the DateTimeOffset property.
I tried:
modelBuilder.Entity<Customer>().Property(c => c.LastModifiedOn).HasColumnType("datetime");
But that threw the Member Mapping specified is not valid error.
I was hoping to be able to put the UtcDateTime DateTimeOffset property value in the DB and when reading have the DateTimeOffset be in UTC (i.e. have an Offset of zero).
No. DateTimeOffset in .NET class will map to DateTimeOffset SQL type. You cannot change this behavior directly because EF doesn't provide simple type conversions / mappings. If you want to store it as DateTime you must hack it.
First define Customer class with trick to expose private property to mapping referenced by #cincura.net in this post:
public class Customer
{
public static class CustomerExpressions
{
public static readonly Expression<Func<Customer, DateTime>> LastModifiedOn = c => c.LastModifiedOnInternal;
}
// Other properties
public DateTimeOffset LastModifiedOn
{
get { return new DateTimeOffset(LastModifiedOnInternal); }
set { LastModifiedOnInternal = value.DateTime; }
}
private DateTime LastModifiedOnInternal { get; set; }
}
Now you have two properties - one is private and holds DataTime which you want to persist to database and one is public exposing DateTimeOffset for your application. Define it in your context:
public class Context : DbContext
{
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().Ignore(c => c.LastModifiedOn);
modelBuilder.Entity<Customer>().Property(Customer.CustomerExpressions.LastModifiedOn).HasColumnName("LastModifiedOn");
}
}
Anyway why you don't use DateTime directly and store it in UTC?