EntityFramework - How to get tables name from Edmx - entity-framework

I'm working with asp.net mvc3.
I have a edmx that was created ADO.NET Entity Data Model. (Data-First)
TestDb.Designer.cs
namespace Test.Web.DataModel
{
public partial class TestDbContext : ObjectContext
{
public ObjectSet<T_Members> T_Members { ... }
public ObjectSet<T_Documents> T_Documents { ... }
....
}
}
T_Members, T_Documents <-- This property is a table of the database.
I want to get a list of this table.
How to get the list of table name from EDMX?

Answer Myself.
TestDbContext context = new TestDbContext();
var tableList = context.MetadataWorkspace.GetItems<EntityType>(System.Data.Metadata.Edm.DataSpace.CSpace);
foreach (var item in tableList)
{
item.Name;
}
To be help for people who have the same problem ...

I think your 'solution' only works when the table and the entity have the same name.
If you would rename your entity to Document (without the prefix) it would fail.
Quote from Microsoft employee:
No, unfortunately it is impossible using the Metadata APIs to get to
the tablename for a given entity. This is because the Mapping metadata
is not public, so there is no way to go from C-Space to S-Space using
the EF's APIs.

Related

How to Customize Migration History Table with Entity Framework Core

I'm setting up Entity Framework Core in a new API to deploy to an existing SQL Server database that is used by Entity Framework 4.6 applications. There is one Migration History table that is shared by other applications, and has 2 fields in it that need to be populated for each entry: ContextKey, and Model. Entity Framework Core does not have a Context Key, and does not save the Model to the Migration History table.
I've already created a HistoryRepository : SqlServerHistoryRepository and configured Entity Framework Core to use it, but the ConfigureTable method only allows you to create additional columns, but not actually populate each record as it gets inserted with custom data. Providing a default value to the column is not a solution.
public class HistoryRepository : SqlServerHistoryRepository
{
public HistoryRepository(HistoryRepositoryDependencies dependencies)
: base(dependencies)
{
}
protected override void ConfigureTable(EntityTypeBuilder<HistoryRow> history)
{
base.ConfigureTable(history);
history.Property<string>("ContextKey")
.HasMaxLength(300);
history.Property<byte[]>("Model");
}
}
services.AddDbContext<MDSContext>(options =>
options.UseLazyLoadingProxies()
.UseSqlServer(
connectionString,
x => x.MigrationsHistoryTable("__MigrationHistory")).ReplaceService<Microsoft.EntityFrameworkCore.Migrations.IHistoryRepository, Burkhart.CoreServices.IncomingOrders.Core.Models.Base.HistoryRepository>()
);
I should be able to provide a custom value for ContextKey and Model dynamically
I looked all over for solutions, but they all show you how to add a column and set a default value, but not how to set a value dynamically. I ended up digging into the ASP.NET Entity Framework Core source code at GitHub for the solution, so that I would share it with everyone else, as I know there are others that are looking for this information:
Just override the GetInsertScript method on the HistoryRepository and insert your custom values. Here is the full solution:
public class HistoryRepository : SqlServerHistoryRepository
{
public HistoryRepository(HistoryRepositoryDependencies dependencies)
: base(dependencies)
{
}
protected override void ConfigureTable(EntityTypeBuilder<HistoryRow> history)
{
base.ConfigureTable(history);
history.Property<string>("ContextKey")
.HasMaxLength(300);
history.Property<byte[]>("Model");
}
public override string GetInsertScript(HistoryRow row)
{
var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string));
return new StringBuilder().Append("INSERT INTO ")
.Append(SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema))
.Append(" (")
.Append(SqlGenerationHelper.DelimitIdentifier(MigrationIdColumnName))
.Append(", ")
.Append(SqlGenerationHelper.DelimitIdentifier(ProductVersionColumnName))
.Append(", [ContextKey], [Model])")
.Append("VALUES (")
.Append(stringTypeMapping.GenerateSqlLiteral(row.MigrationId))
.Append(", ")
.Append(stringTypeMapping.GenerateSqlLiteral(row.ProductVersion))
.Append($", '{ContextConstants.ContextName}.{ContextConstants.ContextSchemaName}', 0x)")
.AppendLine(SqlGenerationHelper.StatementTerminator)
.ToString();
}
}
Here is a link to the source code on github:
https://github.com/aspnet/EntityFrameworkCore/blob/master/src/EFCore.Relational/Migrations/HistoryRepository.cs

Entity Framework Intercept Generate Migration Script

I use Entity Framework 6.2 Code First (.net framework 4.6.1) and I map few entities to view via Table Attribute. It works for select operations and I handle Insert/Update/Delete with writing trigger to view at sql server side. It works as expected, however when I add a new migration, Entity Framework generate RenameTable scripts for used Table Attribute (actuallyis expected behavior for EF). But I want to intercept migration generation and change these entities tableName to original name.
my code like;
[MapToView("Users","UsersView")]
public class User
{
...
}
I wrote MapToView Attribute, this attribute inherited by TableAttribute and pass to second parameter to TableAttribute. I create this Attribute because if I intercept migration generation, return original table name with this attribute parameters.
In this case when I run "add-migration migrationName" it creates migration scripts like this;
RenameTable(name: "dbo.Users", newName: "UsersView");
but i want to create empty migration when I run "add-migration migrationName" script.
anyone can help me?
I solve the problem.
First: Problem is; When I Map Entity to View EF Code-first generate migration with ViewName. This is problem because I want to use View Instead of Table. So I solve problem with this instructions;
1- I Create BaseEntityConfiguration that Inherited from EntityTypeConfiguration and all entity configuration classes are inherited by.
for example:
public class UserConfig: BaseEntityConfiguration<User> //Generic Type is Entity
{
public UserConfig()
{
}
}
2- I Create MapToViewAttribute that inherited by TableAttribute
public class MapToViewAttribute : TableAttribute
{
public string TableName { get; }
public string ViewName { get; }
public MapToViewAttribute(string tableName, string viewName) : base(viewName)
{
TableName = tableName;
ViewName = viewName;
}
}
3- I Use MapToViewAttribute for example User Entity to use View.
[MapToView("User","UserView")]
public class User
{
...
}
And in BaseEntityConfiguration's Constructor I Get Generic Type and custom attributes. If any entity has MapToView Attribute, I pass to TableName parameter to ToTable Method. So at runtime EF uses View for these entities but doesn't create migration with RenameTable for these entities.
protected BaseEntityConfiguration()
{
var baseType = typeof(TEntityType);
var attributes = baseType.GetCustomAttributes(true);
foreach (var attr in attributes)
{
if (attr.GetType() == typeof(MapToViewAttribute))
{
var tableName = ((MapToViewAttribute)attr).TableName;
ToTable(tableName);
}
}
}
Last EF don't use your configuration files, so you must tell the EF to use this in DbContext class's InternalModelCreate method.
My implementation like this;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var typesToRegister = Assembly.GetExecutingAssembly()
.GetTypes().Where(IsConfigurationType);
foreach (var type in typesToRegister)
{
dynamic configurationInstance = type.BaseType != null
&& type.BaseType.IsGenericType
&& type.BaseType.GetGenericTypeDefinition() == typeof(BaseEntityConfiguration<>)
? Activator.CreateInstance(type, culture)
: Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
modelBuilder.Types().Configure(t => t.ToTable(t.ClrType.Name));
BaseDbContext.InternalModelCreate(modelBuilder);
}
But if you use this approach you must create Insert, Update and Delete Triggers/Rule (if you use SQLServer trigger is an option but if you use postgresql rule is better option) because EF uses this view for insert, update and delete operations.

Delete entities in EF that are related

I have two entities. File and Binary. File contains file metadata and Binary contains file content. I want Binary instance be deleted when I remove File instance. I use the following:
public partial class MyEntities : Entities
{
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries<File>().Where(e => e.State == EntityState.Deleted))
{
entry.Reference<Binary>(i => i.FileBinary).EntityEntry.State = EntityState.Deleted;
}
return base.SaveChanges();
}
}
This code does not work. I mean Binary instance is not deleted and also there is no error. Can anyone tell the reason or a better way to do that?
Thanks
You must add more details of your code, like your EF type(code first,model first,schema first) and etc. however if there is a relation between these two entities you can see this(it seems your entities are related, so if there is not relationship you can add it):
code first:
Entity Framework: Delete Object and its related entities
model first and schema first:
Cascading deletes

Mapping Entity Framework Code First to dynamically named tables

I'm currently using EF5 in a project with a legacy database. The legacy application uses dynamically build tables (xxxx_year, yyyy_year) to store "year based data". I've been trying to find a way to dynamically map the ef entities (xxxx, yyyy, etc) to the tables, based on the year property value, but I always end up getting the "The model backing the context has changed since the database was created." error. Can anyone give me some ideas on how to accomplish this ?
I found some old blog posts talking about edm mapping, where we can separate mapping tables based on some property value (kind of horizontal partitioning), but I can't find any pointers on how to accomplish the same using code first.
Thanks, P
In your mapping configuration for each domain object, you can tell EF that the corresponding table name for an entity is different from the entity name itself.
If your class is called YyyyYear, it can point to a table called "2012_year" by specifying the name in its mapping file.
e.g.
// 1 entity class per db table
public class YyyyYear
{
public int Id { get; set; }
}
// 1 mapping file for entity
using System.Data.Entity.ModelConfiguration;
public class YyyyYearMap: EntityTypeConfiguration
{
public YyyyYearMap()
{
this.HasKey(t => t.Id);
this.ToTable("2012_year");
}
}
// your db context class (derives from DbContext)
using System.Data.Entity;
public class MyDbContext: DbContext
{
// 1 db set for every entity/table
public DbSet YyyyYears { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// 1 mapping file for every entity/table
modelBuilder.Configurations.Add(new YyyyYearMap());
}
}
I'm not sure if that's what you're looking for, but I have a blog post with step-by-step instructions, a working sample, and how to resolve common issues.
http://wakeupandcode.com/entity-framework-code-first-migrations/
Hope this helps!

entity framework adding the contents of one data table to another one in the same database

How do I add the records of one data table to another with the entity framework?
With data sets its like:
private void sourceTabletransfer()
{
foreach (DataRow sourceTableRow in myDataSet.Tables["sourceTable"].Rows)
{
DataRow destinationTablerow = myDataSet.Tables["destinationTable"].NewRow();
destinationTablerow["date"] = sourceTableRow["date"];
destinationTablerow["varchar1"] = sourceTableRow["varchar1"];
destinationTablerow["int1"] = sourceTableRow["int1"];
myDataSet.Tables["destinationTable"].Rows.Add(destinationTablerow);
}
this.destinationTableBindingSource.EndEdit();
this.destinationTableTableAdapter.Update(myDataSet);
}
How do I do the above with the entity framework?
Thanks a lot in advance :)
Say you have two tables and their corresponding PoCo classes. Named them ParentEntity and ChildEntity.
foreach(var parentEntity in lstParentEntities)
{
ChildEntity child=new ChildEntity();
child.prop1=parentEntity.Prop1;
child.prop2=parentEntity.Prop2;
child.prop3=parentEntity.Prop3;
context.AddObject(child);
}
context.SaveChanges();
An Alternative -
Working with your POCO classes named Source and Destination;
context.Destination.Prop1 = Source.Prop1
context.SaveChanges();
Also - (I would add this as a comment if i had access to it :))
you need to use context.SaveChanges(), not context.Save()