I'm using Entity Framework 5 (with Code First approach) to populate a class of mine from a legacy stored procedure with parameters, and this is working fine (details follow). My problem is that I want to map the columns' names to property with different names (I don't like the names coming from the Erp). I tried to use a Configuration class (like what I do when I map to views or tables) to specify the column name for properties with a different name, and here are my results:
Why the configuration is affecting my result but its specification are not used to map the columns? Is there another way to specify this mapping using SqlQuery?
Here are the details:
My POCO class:
public class Item
{
public String Id { get; set; }
public String Description { get; set; }
}
The configuration class:
public class ItemConfiguration : EntityTypeConfiguration<Item>
{
public ItemConfiguration()
{
HasKey(x => new { x.Id });
Property(x => x.Id).HasColumnName("Code");
Property(x => x.Description).HasColumnName("ItemDescription");
}
}
The stored procedure return the data with the columns "Code" and "ItemDescription"; I call it in this way:
var par = new SqlParameter();
par.ParameterName = "@my_par";
par.Direction = ParameterDirection.Input;
par.SqlDbType = SqlDbType.VarChar;
par.Size = 20;
par.Value = ...;
var data = _context.Database.SqlQuery<Item>("exec spItem @my_par", par);
and with this I add the configuration to the context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ItemConfiguration());
}
Thank you!
To use code-first for an existing database, right click on your project in Visual Studio -> Add -> New Item.. Select ADO.NET Entity Data Model in the Add New Item dialog box and specify the model name (this will be a context class name) and click on Add.
Map Entity to Table. Code-First will create the database tables with the name of DbSet properties in the context class, Students and Standards in this case. You can override this convention and give a different table name than the DbSet properties, as shown below.
There is no way to convert your code-first classes into database-first classes. Creating the model from the database will create a whole new set of classes, regardless of the presence of your code-first classes. However, you might not want to delete your code-first classes right away.
I found here:
http://entityframework.codeplex.com/workitem/233?PendingVoteId=233
that "The SqlQuery method is designed not to take any mapping into account ...".
They say also "We agree that it would be useful to have the option to make SqlQuery honor Column attributes so we're keeping this issue open and putting it on our backlog for future consideration.", so, if you have my same problem, please vote :-)
Meanwhile, you can use this method. Few tests (because it worked for my classes) but not to difficult to fix if needed... It need a context (to retrieve mapped custom types) and it need a different connection to run a datareader on it at the same time.
Usage:
List students = Mapper.Map(context, (new SchoolContext()).Database.Connection, "Select * from Students");
public static class Mapper
{
/// <summary>
/// Maps the result of a query into entities.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="context">The context.</param>
/// <param name="queryConnection">The connection to run the query. Must be different from the one on the context.</param>
/// <param name="sqlQuery">The SQL query.</param>
/// <returns>An entity list</returns>
/// <exception cref="System.ArgumentNullException">
/// context
/// or
/// queryConnection
/// or
/// sqlQuery
/// </exception>
public static List<T> Map<T>(DbContext context, DbConnection queryConnection, string sqlQuery) where T:new()
{
if (context == null)
throw new ArgumentNullException("context");
if (queryConnection == null)
throw new ArgumentNullException("queryConnection");
if (sqlQuery == null)
throw new ArgumentNullException("sqlQuery");
var connectionState = queryConnection.State;
if (connectionState != ConnectionState.Open)
queryConnection.Open();
DbCommand command = queryConnection.CreateCommand();
command.CommandText = sqlQuery;
DbDataReader reader = command.ExecuteReader();
List<T> entities = new List<T>();
while (reader.Read())
{
entities.Add(InternalMap<T>(context, reader));
}
if (connectionState != ConnectionState.Open)
queryConnection.Close();
return entities;
}
private static T InternalMap<T>(DbContext context, DbDataReader reader) where T: new()
{
T entityObject = new T();
InternalMapEntity(context, reader, entityObject);
return entityObject;
}
private static void InternalMapEntity(DbContext context, DbDataReader reader, object entityObject)
{
ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
var metadataWorkspace = ((EntityConnection)objectContext.Connection).GetMetadataWorkspace();
IEnumerable<EntitySetMapping> entitySetMappingCollection = metadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().EntitySetMappings;
IEnumerable<AssociationSetMapping> associationSetMappingCollection = metadataWorkspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace).Single().AssociationSetMappings;
var entitySetMappings = entitySetMappingCollection.First(o => o.EntityTypeMappings.Select(e => e.EntityType.Name).Contains(entityObject.GetType().Name));
var entityTypeMapping = entitySetMappings.EntityTypeMappings[0];
string tableName = entityTypeMapping.EntitySetMapping.EntitySet.Name;
Console.WriteLine(tableName);
MappingFragment mappingFragment = entityTypeMapping.Fragments[0];
foreach (PropertyMapping propertyMapping in mappingFragment.PropertyMappings)
{
object value = Convert.ChangeType(reader[((ScalarPropertyMapping) propertyMapping).Column.Name], propertyMapping.Property.PrimitiveType.ClrEquivalentType);
entityObject.GetType().GetProperty(propertyMapping.Property.Name).SetValue(entityObject, value, null);
Console.WriteLine("{0} {1} {2}", propertyMapping.Property.Name, ((ScalarPropertyMapping)propertyMapping).Column, value);
}
foreach (var navigationProperty in entityTypeMapping.EntityType.NavigationProperties)
{
PropertyInfo propertyInfo = entityObject.GetType().GetProperty(navigationProperty.Name);
AssociationSetMapping associationSetMapping = associationSetMappingCollection.First(a => a.AssociationSet.ElementType.FullName == navigationProperty.RelationshipType.FullName);
// associationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings contains two elements one for direct and one for inverse relationship
EndPropertyMapping propertyMappings = associationSetMapping.AssociationTypeMapping.MappingFragment.PropertyMappings.Cast<EndPropertyMapping>().First(p => p.AssociationEnd.Name.EndsWith("_Target"));
object[] key = propertyMappings.PropertyMappings.Select(c => reader[c.Column.Name]).ToArray();
object value = context.Set(propertyInfo.PropertyType).Find(key);
propertyInfo.SetValue(entityObject, value, null);
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With