I've this code and everything works just fine.
using (var db = new ApplicationDbContext())
{
md = db.Modles.ToList();
}
My question is that I have a parameter called M and that is the name of a Model I've created, so it can be dynamic.
Is there a way to do something like this:
var M = "Modles"; // or M = "Modles2"
using (var db = new ApplicationDbContext())
{
md = db[M].ToList();
}
I've tried Entity Framework Get Table By Name and Entity Framework get table by variable name but without any luck.
Can this be done?
The solution on second link you provided actually doesn't work because it's missing a simple step: The method Set on DbContext is a generic method, so you cannot simply invoke it without telling the type, either at compile time or at least at runtime. Since you want it to be called dynamically, runtime resolution is the option you're left with. The following example should work:
var entityNs = "myentities";
var table = "Model";
var entityType = Type.GetType($"{entityNs}.{table}");
var methodInfo = typeof(ApplicationDbContext).GetMethod("Set");
var generic = methodInfo.MakeGenericMethod(entityType);
dynamic dbSet = generic.Invoke(myContext, null);
var list = dbSet.GetList();
Reference: How do I use reflection to call a generic method?
Related
I have an application that is using MVC framework that currently reads from a model object of a SQL table and puts the results in a list format. The table has its own .cs class with all the necessary fields and the code filters the results based on other factors.
The problem I am running into, is that I need to find a way to add new tables to this list without making changes to the code itself. Ideally, I would like to add a list of tables I need to read from into the web.config and create a class for them in the project structure and the code will dynamically read from that.
Currently with one table the code looks like this:
var assets = _model.FMIF.ToList();
var results = BuildFormatedResult(assets);
And I have been able to add another table to this structure like this:
var assets = _model.FMIF.ToList();
var assets2 = _model.FMHZ.ToList();
assets = assets.Concat(assets2).ToList();
var results = BuildFormatedResult(assets);
I have tried to make this dynamic using a method like this:
var test = "FMIF";
var assets = _model.+test+.ToList();
var results = BuildFormatedResult(assets);
but it does not appear to be able to read the variable as a model.
Is there a best practice way to do something like this? Even if it is way different than what I have tried I am kind of at a loss here. Not super familiar with MVC structure so any help or advice is greatly appreciated.
Thanks!
This is an approach I've used to dynamically get a handle to a DbSet. Firstly I define a couple of methods (This does assume that you have the classes already generated for each of the tables.)
public static DbSet GetDbSet(MyDbContext db, string tableName)
{
// Find the EF entity that corresponds to this table
ObjectContext objectContext = (db as IObjectContextAdapter).ObjectContext;
var mappings = GetEntityMappings(objectContext);
var entityName = mappings[tableName];
// Now get the corresponding DbSet
DbSet dbSet = (DbSet)db.Set(Type.GetType("schema_name." + entityName));
return dbSet;
}
public static Dictionary<string, string> GetEntityMappings(ObjectContext objectContext)
{
var EntityMappings = new Dictionary<string, string>();
// Build a list of database table names to EF entity names
// Get a list of entities
MetadataWorkspace workspace = objectContext.MetadataWorkspace;
var entities = workspace.GetItems<EntityType>(DataSpace.CSpace);
foreach (EntityType et in entities)
{
// Get the entity set that uses this entity type
var entitySet = workspace
.GetItems<EntityContainer>(DataSpace.CSpace)
.Single()
.EntitySets
.Single(s => s.ElementType.Name == et.Name);
// Find the mapping between conceptual and storage model for this entity set
var mapping = workspace.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
.Single()
.EntitySetMappings
.Single(s => s.EntitySet == entitySet);
// Find the storage entity set (table) that the entity is mapped to
var table = mapping
.EntityTypeMappings.Single()
.Fragments.Single()
.StoreEntitySet;
string tableName = (string)table.MetadataProperties["Table"].Value ?? table.Name;
EntityMappings.Add(tableName, et.Name);
}
return EntityMappings;
}
Basically it is poking around in the EntityFramework metadata in order to map a string of a table name to the entity name (which aren't necessarily the same). This is slightly simplified as ideally you would cache the mappings.
Then finally call the GetDbSet() method, I've used AsQueryable() as I potentially need to add where clauses etc.
var dbSet = GetDbSet(db, "table_name").AsQueryable();
I am having some (more) issues with my Telerik OpenAccess ORM. This time in the area of applying Fetch Strategies to a query. Here's the code ...
using (DAL.DarkRoomDB ctx = new DarkRoomDB())
{
//
// Set the resulting object to include the contents of the package
FetchStrategy strategy = new FetchStrategy();
strategy.LoadWith<DeliverablePackageEntity>(c => c.PackageContents);
strategy.LoadWith<DeliverablePackageContentEntity>(c => c.Deliverable);
strategy.LoadWith<DeliverablePackageContentEntity>(c => c.DeliverablePackage);
strategy.MaxFetchDepth = 3;
ctx.FetchStrategy = strategy;
//
// get the package that matches the SKU
DataStoreRepository<DeliverablePackageEntity> repo = DataStoreRepository<DeliverablePackageEntity>.GetInstance(ctx);
DeliverablePackageEntity entity = repo.GetEntityList(c => c.PackageSku == SKU).FirstOrDefault();
//
// Create a DISCONNECTED COPY of the entity
copy = ctx.CreateDetachedCopy<DeliverablePackageEntity>(entity, strategy);
}
ret = EntityToDomainMapper.Map<DeliverablePackageEntity, DeliverablePackage>(copy);
return ret;
When I run this I expect the PackageContents of the DeliverablePackageEntity to be pre-loaded. When I look at the entity variable in the debugger however it tells me that the "contents of the property will be enumerated when expanded" which would suggest to me that the property has not yet been pre-populated, which is what I thought the purpose of the FetchStrategy was.
Am I missing something?
This behavior is normal because the navigation properties of the entity object are of type IEnumerable. Therefore even if they are preloaded in memory, you need to enumerate through them in order to access them.
You can verify that the navigation properties specified in the FetchStrategy have been preloaded by checking the whether there is SQL script generated when accessing them.
Consider the following example where the related RentalOrders for the Car object are preloaded. Upon executing the ToList() method they will be enumerated but the executedScript will remain empty because they have been preloaded by the FetchStrategy:
using (EntitiesModel1 context = new EntitiesModel1())
{
FetchStrategy loadWithRentalOrders = new FetchStrategy();
loadWithRentalOrders.LoadWith<Car>(car => car.RentalOrders);
context.FetchStrategy = loadWithRentalOrders;
Car firstCar = context.Cars.First();
context.Log = new StringWriter();
List<RentalOrder> relatedOrders = firstCar.RentalOrders.ToList();
//should be empty
string executedScript = context.Log.ToString();
}
I hope this helps.
When I create new entities in a context, I cannot get a proxy even if I request it again. I have to dispose of the context and make a new one. Is this an expected behavior or am I doing something wrong?
int id = 0;
using (var context = new MyContext())
{
var A = context.People.Add(new Person{ Name = "Bob" }); // A is not a proxy
context.SaveChanges(); // A is still not a proxy
var B = context.People.Where(o => o.Id == A.Id).Single(); // B is not a proxy
id = A.Id; // Keep the ID to use with a new context
}
using (var context = new MyContext())
{
var C = context.People.Where(o => o.Id == id).Single(); // C is a proxy!
}
You can use the Create method of your DBSet:
var newPerson=context.People.Create();
The instance returned will be a proxy if the underlying context is configured to create proxies and the entity type meets the requirements for creating a proxy.
Update
As #Asad said, if you are creating a new entity, you need to add it to your DBSet after creating it:
context.People.Add(newPerson);
Or change its State to Added, for example:
context.Entry(newPerson).State = EntityState.Added;
And, if you are updating it, then you should use Attach method:
var existingPerson=context.People.Create();
existingPerson.Id = 1; // assuming it exists in the DB
context.People.Attach(existingPerson);
octavioccl's answer is correct but would force me to break my data layer pattern. I have an alternative (probably slower) solution that didn't force me to create the entity inside my repository nor required me to add mapping everywhere (or a mapping library). I am still accepting your answer since it probably is closer to best practice and did answer the original question but I wanted to add this option if ever needed.
dbContext.People.Add(person);
dbContext.SaveChanges();
dbContext.Entry(person).State = EntityState.Detached;
dbContext.People.Find(person.Id);
I am still a beginner at writing C# and SQL and was wondering if someone could help me with this basic question. I have looked all over the internet and am still stuck.
I am trying to write a WCF service to access my database. I only need one method:
public PathDto GetPath(string src, string trg)
{
using (var context = new PathsEntities())
{
var p = (
from a
in context.src
where a.Target = trg
select a).Distance, Path;
}
}
where the parameter src is the table name, and the string trg is the entity's primary key.
Visual studio gives me the error: ...pathsService does not contain a definition for src because it is trying to look up the table "src" and not the string contained in the variable.
How can I use my parameter in the lookup statement?
I am going to assume you are using DbContext EF5.0 stuff
public PathDto GetPath(string tableType, string id)
{
using (var context = new PathsEntities())
{
var type = Type.GetType(tableType);
var p = context.Set(type).Find(id);
return (PathDto)p;
}
}
Seems you DON'T use EF 5.0 and have only got EF 4.0 and are using ObjectContext. Try this...no idea if it works since I don't really use EF 4.0. Alternatively download EF 5.0
public PathDto GetPath(string tableType, string id)
{
using (var context = new PathsEntities())
{
var type = Type.GetType(tableType);
var p = context.GetObjectByKey(new EntityKey(tableType, "id", id));
return (PathDto)p;
}
}
So I'm attempting to dynamically load my domain data service where the table name is the string ... Here's what I've got so far: Normally I'd load like this:
theDomainDataService.Load(theDomainDataService.getUsersQuery());
so I'm trying to automate which entity is loaded by the string name.
String theVariableEntityName = "Users";
Type t = theDomainDataService.GetType();
MethodInfo stuff = t.GetMethod("Get" + theVariableEntityName + "Query");
var theQuery = stuff.Invoke(theDomainDataService, null);
theDomainDataService.Load((EntityQuery<MySite.Web.Models.User>)theQuery);
---------------------------------------------------------^ Problem
This is in fact loading my domainDataService correctly, but what I need is a dynamic way to infer the type of the EntityQuery (without explicitly declaring it's going to be a User), because in fact it could be anything.
I have tried this from the DomainDataService Class with no luck, it isn't finding method's "Set" or "Entry".
public List<object> void PopulateEntity(string theEntityName)
{
Type theEntity = Type.GetType("MySiteMaintenance.Web.Models." + theEntityName);
using (var db = new DatingEntities())
{
IQueryable query = db.Set(theEntity);
foreach (var item in query)
{
var entry = db.Entry(item);
}
}
}
Remember, all I need is a populated entity (when all I have is the name of the entity) populated Client side... so I can say
DomainServiceClass theClass = new DomainServiceClass();
theClass.Load(theClass.GetEntityNameQuery());
so I can reference the appropriately loaded entity with...
theClass.Entity (users... questions, etc..)
I'm still not sure I follow, but...
I have a Post entity in my Sandbox namespace which I get from my DbContext instance using the entity type name in a string to start with...
// Get my entity type (if in same assembly, else you'll have to specify)
Type postType = Type.GetType("Sandbox.Post");
using (var db = new StackOverflowEntities()) {
// not required
db.Configuration.ProxyCreationEnabled = false;
IQueryable query = db.Set(postType);
foreach (var item in query) {
DbEntityEntry entry = db.Entry(item);
}
}
Which results in retrieving any DbSet based on a Entity type string. Below a breakpoint in the item foreach loop - revealing the values.