Entity Framework Core - Get DbContext data model - entity-framework

I need to know information about entities, tables, mappings, keys etc for the given instance of DbContext. In Entity Framework 6 I was writing edmx like this:
System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(dbContext, xmlWriter);
which I then used to build my own data model (this is needed for a tool which supports loading data from different sources). How do I get such information for the new EF Core (previous EF 7)? I could use Reflection, but this will give me only the conceptual schema, while I also need mappings and storage schema. I've been looking through the EF source code for a while now, but don't seem to find any object, that stores all the required data.

This should get you started
using (var ctx = new TestContext())
{
var entityType = ctx.Model.FindEntityType(typeof (Entity_Basic));
var tableName = entityType.SqlServer().TableName;
var columnName = entityType.GetProperties().ToList()[0].SqlServer().ColumnName;
}

Related

How to use a SQL query result as an object in Entity Framework?

I'm creating a data analysis process in c# using EF (code first). The data model involves about a dozen interrelated classes. Most of these directly correspond with tables in an existing database, but a couple key read-only object types are really the product of complex, multi-step SQL queries. I can't create a view or stored proc for the query.
I'd like to be able to use something like DbSet.SqlQuery() to load those query results into EF objects, and still leverage EF's ORM features to relate them with the rest of the object graph.
I think what I want is a way to override EF's SQL SELECT code when the DbSet tries to populate that query-based object from the database. Is this possible? Is there a better alternative approach?
Could you perhaps write your arbitrary query into a stored procedure? You could then use Entity framework to map that to a function.
I've found a way that's working for me, but it's hacky. (Is there a better way?)
Let's say Foo is a poco class that corresponds with an expensive query result that I want to explicitly write in SQL and use as an object in a read-only EF object graph. Foo includes foreign keys and navigation properties. My dbContext has a DbSet called Foos.
// Pre-req: prevent EF from trying to sync model with db, since Foos doesn't map to a db table.
public FooDbContext()
{
Database.SetInitializer<FooDbContext>(null);
}
// load the query data with SqlQuery() and Attach()
var foos = db.Foos.SqlQuery("<hand written SQL SELECT>", parameters);
foreach(var foo in foos) db.Foos.Attach(foo);
// Subsequently reference the data with .Local to prevent db hits (which would fail, since no Foo table in db.)
var result = db.Foos.Local.Select(...);
I do have to explicitly load related objects, but then all the relationship association happens automatically. For example let's say Bar has a 1-to-many with Foo, Foo has a BarId property (and a Bar navigation property) and maybe the Bar class has an observable collection of Foos. If Bars is stored as a db lookup table, I could just do db.Bars.Load(), or something more filtered like:
var barIds = db.Foos.Local.Select(f => f.BarId).Distinct().ToArray();
db.Bars.Where(b => barIds.Contains(b.barId)).Load();
Then I can use Foo.Bar or Bar.Foos and everything works.

DTO/POCO with Entity Framework

I'm using EF5 Model First. I don't really understand what are the auto-generated classes from the EDM. According to some documentation this classes are POCOs but why are they used in the context ?
Assuming I have a Student entity, then I get a Student POCO class and a DbSet StudentSet property in my context.
Will this next instructions put a POCO in my database ?
MyContext.StudentSet.Add(johndoe);
MyContext.SaveChanges();
So EF uses POCO to transfer data ? Actually I miss the step when POCO exchange data with entities or DTO and when the entities put data in the database.
The generated classes from the EDM is the ORM / Persistence classes. You use that classes to query / make changes from / to database. You need to translate any DTO object to POCO object when about making changes to database.
ORM is about mapping object to data in database, instead of dealing with insert into syntax to insert record to database in the application, you use StudentSet.Add to add a new data. The johndoe information will be translated into sql syntax, EF will map each property to each column when translating it into query.
The Add method will store the johndoe information as Added in the memory but it will not be executed right away to the database. If you have another Add method, it will be marked as Added too. The moment you call SaveChanges, all the changes will be saved into database by sending a generated query.
The mapping between DTO and EF entity happens before you add the johndoe. You might have another DTO class that is used in the UI. You need to map it manually or using mapper library to create a POCO object from a DTO object. For example:
// studentDto as parameter
var johndoe = new Student
{
Name = studentDto.StudentName,
Age = studentDto.StudentAge
};
MyContext.StudentSet.Add(johndoe);
// studentDto might have another information as well
var johndoeSubject = new Subject
{
Name = studentDto.SubjectName,
Years = studentDto.SubjectYears
};
MyContext.SubjectSet.Add(johndoeSubject);
MyContext.SaveChanges();

How to get fluent api mapped entity table name EF

I'm having problems getting the mapped table name when using fluent api. When you use data annotations it's easy because you can use the TableAttribute in order to get the table name from an specific entity, but when I use fluent api I can't find a way to do that.
I need it because I'm implementing an audit trail overriding the SaveChanges on the dbContext.
Thanks
I figure it out.
In order to get information about the Entity when using Fluent API I did this:
// get the actual entity Set
// DataSpace.SSpace = Storage part of the model has info about the shape of our tables
var entitySet = workspace.GetItems<System.Data.Entity.Core.Metadata.Edm.EntityContainer>(System.Data.Entity.Core.Metadata.Edm.DataSpace.SSpace)
.SelectMany(e => e.EntitySets).ToList().Where(a => a.Name == entityTypeName).FirstOrDefault();
And finally:
// Get table name
string tableName = entitySet.Table;
Hope this can help others!

Restricting lazy loading while automapping EF entity type

Folks,
I am mapping EF objects to simple Pocos using automapper in my service layer. I have certain entities that have many relationships in EF,but I want to restrict how much of this gets pulled back from the db. One of these entities would map to a database table but would have many relationships to other tables that would appear as entity collections in the generated model (EDMX).
So I have created a "shallow" poco for the entity which only has poco properties for the first level of properties in the entity, i.e. some integer Ids instead of associated collections/entity collections. I map in the following manner....
var simplepocoforentity= Mapper.Map(_readOnlyDb.Single<EfEntity>(x => x.Id== Id),
new SimplPocoForEntity());
My question is.. because I am only mapping the entity to a simple poco here, can I be confident that EF will not try and query other non referenced data from the underlying db tables relationships when I do the mapping?
I know that I can investigate this via SQL Profiling, but I would appreciate any input befor going down that route.
Thanks
K.
It depends on the internal implementation of AutoMapper. I would assume that AutoMapper does not try to access the navigation properties of the entity (in which case lazy loading and an additional database query would kick in). I don't see a reason why AutoMapper should do this if you only map scalar properties. But who knows...
If you want to be on the safe side you could disable lazy loading temporarily:
try
{
_readOnlyDb.ContextOptions.LazyLoadingEnabled = false;
var simplepocoforentity = Mapper.Map(_readOnlyDb.Entities
.Single(x => x.Id == Id), new SimplPocoForEntity());
// ...
}
finally
{
_readOnlyDb.ContextOptions.LazyLoadingEnabled = true;
}
As a side note: Be aware that you load the full entity into the context with this approach. After that the mapping happens. Potentially you load more columns from the database than you really need (perhaps not in your special case here). In general I would do the mapping with a projection which ensures that the database only queries the columns which are needed:
var simplepocoforentity = _readOnlyDb.Entities
.Where(e => e.Id == Id)
.Select(e => new SimplPocoForEntity
{
PocoProperty1 = e.EntityProperty1,
PocoProperty2 = e.EntityProperty2,
PocoProperty3 = e.EntityProperty3
// etc.
})
.Single();
With this approach no lazy loading would happen at all because you don't load the entity but directly the PocoForEntity from the database.

Entity framework: Database first/Code first hybrid

I am trying to create a custom Entity Framework (4.2) entity that would be mapped to my database like it would be done in a Code first approach.
The issue is that my entity framework data model is using Database first.
How can I add my custom entity to entity framework's context?
If by the Database first you mean that you already have EDMX created from exiting database you simply cannot use code first. You must create table and update model (EDMX) from the database to include it in EDMX.
Edit based on comment:
I want to create a BriefUser entity that would basically be a lighter
version of User but it would have properties retrieved from User's
foreign keys.
Well this is possible. You can either create BriefUser as common class and use projection in query.
var breifUser = (from x in context.Users
where ...
select new BriefUser
{
// Fill BreifUser's properties here
}).FirstOrDefault();
You can even refactor former code to reusable extension method:
public static IQueryable<BriefUser> ProjectUser(this IQueryable<User> query)
{
return query.Select(x => new BreifUser()
{ // Fill BreifUser's properties here });
}
and use it like:
var briefUser = context.Users.ProjectUser().FirstOrDefault(...);
It is also possible to define your new class as "entity view". The first problem is that each table can be mapped to only one entity (except some advanced concepts like inheritance or splitting) so you cannot define your BriefUser as a new entity type because mapping both User and BriefUser to UserTbl would violate this rule. You must use special construct called QueryView.
QueryView is view in mapping level. It allows you to create new mapped type which is projection of existing mapped entities defined directly in EDMX's MSL part. The projection is defined as custom Entity SQL query. The problem is that QueryView has limitations:
It doesn't offer all Entity SQL features - for example it doesn't support aggregations (which I consider as really missing feature). Without aggregations you for example cannot create a new type which will contain property counting some related entities.
It is not supported in designer. You must edit your EDMX as XML to define QueryView and you must write Entity SQL query yourselves.
Resulting type is a "view" and it is read-only.
I want to keep the EDMX file, but also be able to add an entity
(BriefUser) to EF's context.
This is not possible. Your BreifUser is only projection / view and EF is not able to track changes back to originating tables so you cannot add BreifUser to context and persist it. In case of QueryView you can achieve it if you define custom stored procedures which will no how to decompose BreifUser and modify all related tables. These stored procedures must be imported to the EDMX and mapped to data modification operations of the view entity. Btw. same will happen if you map your entity to the database view because EF takes all views as read-only.