I have been working in a project which uses Entity Framework as the ORM data model to connect to the SQL database and retrieve data.
Now the basic query which is used to retrieve data is like this
ProjectDataContext dataContext = new ProjectDataContext();
var result = (from project in dataContext.Projects
select project).ToList();
Or in lambda
List<Project> lstprojects = dataContext.Projects.Take(10);
Now I would like to pass the table name dynamically based on some input parameter. How can I achieve that?
The way I am currently doing it is a bit messy.
if(tableName = "A")
{
List<A> lstOfA = dataContext.A.Take(10);
}
else if(tableName = "B")
{
List<B> lstOfB = dataContext.B.Take(10);
}
and so on...
My question is if there is a neat and clean way to do this without writing so many if else because I understand it may cause performance issues in future.
Thanks
Ok after some trial and error I have been able to do it like this-
var type = Type.GetType("A");
context.Set(type).Load();
var result = context.Set(type).Local.Cast<object>().ToList();
Hope it helps.`
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 currently have a Class which uses the function as such:
var txbl = test.search_bustype("SUP", "Name");
or
foreach(string toWorkWith in test.search_bustype("SUP", "Name")){ // each one }
However, for every Column I want to search using a function, I have to create a separate function.
ie: Columns - bustype, companyID - Would have to have separate functions to search.
My current code is:
public Array search_bustype(string match, string forthat)
{
db = new rkdb_07022016Entities2();
var tbl = (from c in db.tblbus_business select c).ToArray();
List<string> List = new List<string>();
int i = 0;
foreach (var toCheck in tbl)
{
if (toCheck.BusType.ToString() == match)
{
if (forthat == "Name")
{
List.Add(toCheck.Name);
}
}
i++;
}
return List.ToArray();
}
Is there anyway to possibly, like php actually send the query to the function and then run it there? I haven't been able to find many sources about how to build a secure infrastructure with Entity so I am wondering if anyone knows any way of maybe creating a skeleton method with this framework.
Thanks in advance!
Okay so I stumbled on the Frameworks sources and actually now understand that the Framework itself implements the Skeleton method.
You simply only refer to each query inside the (from c in......
I'll have to look further into how this infrastructure works before I can understand how to further implement functions.
Thank-you for your time however! I will close this.
I have used Automapper for a long time without any difficulty for a long time and I have been trying to integrate this wonderful tool into my LINQ to SQl framework recently. I have following code:
using (var ps = new Promotionalsponsorship(constr))
{
var applicationToSave = Mapper.Map<ApplicationModel, Application>(application);
if (applicationToSave.ApplicationId == default(int))
{
ps.Application.InsertOnSubmit(applicationToSave);
}
ps.Application.Context.SubmitChanges();
}
What I am trying to do here is UPSERT method where I am saying that if there is anew record with id = 0 then insert OR update. Funny thing here is that insert works BUT ps.Application.Context.SubmitChanges(); does not work when I update the context using Automapper as I did that in the code above.
Now, if I update context using simple property assignment like following:
applicationToSave.Name = "Beautiful";
if (applicationToSave.ApplicationId == default(int))
{
ps.Application.InsertOnSubmit(applicationToSave);
}
ps.Application.Context.SubmitChanges();
This one works. I feel like Automapper does something to context object which makes it detached from the DB. I am not sure. How can I make it work with AutoMapper?
Change tracking only works for loaded entities. Automapper is creating a new object for you, which is completely unrelated to EF - it's just a normal object as far as the code cares. You'll need to do something like this:
using (var ps = new Promotionalsponsorship(constr))
{
var applicationToSave = ps.Application.Find(application.ApplicationId);
if (applicationToSave == null)
applicationToSave = new Application();
Mapper.Map(application, applicationToSave); //May not be accurate, search for the
//method that writes into an existing object
if (applicationToSave.ApplicationId == default(int))
{
ps.Application.InsertOnSubmit(applicationToSave);
}
ps.Application.Context.SubmitChanges();
}
That is, you load the existing application (or create a new one), then fill it with automapper.
I've been learning C# / LINQ / ASP.NET / MVC 3 / EF for a few months now comming from Java / Icefaces / Ibatis base (Real world uses .NET D;). I really enjoy LINQ / Entity Framework from the .NET Framework but I'm having a few issues understand what's really happening behind the scenes.
Here's my problem:
I'm using a AJAX / JSON fed jQuery datatable (that I highly recommend to anyone in need of a free web datatable system by the way). I have a method in my MVC3 application that returns a JSON result of the data needed by the table, doing the sorting and all. Everything is working nicely and smoothly. However, I'm having a concern with the "dirty" hack I had to do to make this work.
Here's the complete code:
//inEntities is the Entity Framework Database Context
//It includes the following entities:
// Poincon
// Horaire
// HoraireDetail
//Poincon, Horaire and HoraireDetail are "decorated" using the Metadata technic which
//adds properties methods and such to the Entity (Like getEmploye which you will see in
//the following snippet)
//
//The Entity Employe is not a database data and therefor not handled by the EF.
//Instead, it is a simple object with properties that applies Lazy Loading to get an
//Employe Name based off of his Employe ID in the Active Directory. An employe object
//can be constructed with his Employe ID which will expose the possibility of getting
//the Employe Name from the AD if needed.
[HttpPost]
public JsonResult List(FormCollection form)
{
String sEcho;
int iDisplayStart;
int iDisplayLength;
String sSearch;
int iSortingCols;
Dictionary<String, String> sorting;
try
{
sEcho = form["sEcho"];
iDisplayStart = int.Parse(form["iDisplayStart"]);
iDisplayLength = int.Parse(form["iDisplayLength"]);
sSearch = form["sSearch"];
iSortingCols = int.Parse(form["iSortingCols"]);
sorting = new Dictionary<string,string>();
for (int i = 0; i < iSortingCols; i++)
sorting.Add(form["mDataProp_" + form["iSortCol_" + i]].ToUpper(), form["sSortDir_" + i].ToUpper());
}
catch
{
HttpContext.Response.StatusCode = 500;
return null;
}
var qPoincon = inEntities.Poincons.AsEnumerable();
var lPoincon = qPoincon.Select(o => new
{
o.id,
emp = o.getEmploye(),
o.poinconStart,
o.poinconEnd,
o.commentaire,
o.codeExceptions
}).AsEnumerable();
//Search
lPoincon = lPoincon.Where(p => (p.emp.empNoStr.Contains(sSearch) || p.emp.empNom.Contains(sSearch) || (p.commentaire != null && p.commentaire.Contains(sSearch))));
//Keep count
int iTotalDisplayRecords = lPoincon.Count();
//Sorting
foreach(KeyValuePair<String,String> col in sorting)
{
switch (col.Key)
{
case "EMPNO":
if (col.Value == "ASC")
lPoincon = lPoincon.OrderBy(h => h.emp.empNo);
else
lPoincon = lPoincon.OrderByDescending(h => h.emp.empNo);
break;
case "POINCONSTART":
if (col.Value == "ASC")
lPoincon = lPoincon.OrderBy(h => h.poinconStart);
else
lPoincon = lPoincon.OrderByDescending(h => h.poinconStart);
break;
case "POINCONEND":
if (col.Value == "ASC")
lPoincon = lPoincon.OrderBy(h => h.poinconEnd);
else
lPoincon = lPoincon.OrderByDescending(h => h.poinconEnd);
break;
case "COMMENTAIRE":
if (col.Value == "ASC")
lPoincon = lPoincon.OrderBy(h => h.commentaire);
else
lPoincon = lPoincon.OrderByDescending(h => h.commentaire);
break;
}
}
//Paging
lPoincon = lPoincon.Skip(iDisplayStart).Take(iDisplayLength);
//Building Response
var jdt = new
{
iTotalDisplayRecords = iTotalDisplayRecords,
iTotalRecords = inEntities.Poincons.Count(),
sEcho = sEcho,
aaData = lPoincon
};
return Json(jdt);
}
As you can see, when I'm grabbing the entire list of "Poincons" from the EF and turning it into a Enumerable. From my current understanding, turning the LINQ query into a Enumerable "kills" the link to the EF, or in other words, will generate the SQL required to get that list at that point instead of keeping the LINQ data until the end and execute a percise query that will return only the data you require. After turning this LINQ Query into a Enumerable, I'm heavily filtering the LINQ (since there is paging, sorting, searching in the datatable). This leads me to thinkg that what my code is currently doing is "Grab all the "Poincons" from the database and put it into the web server's memory as a Enumerable, do your work with the Enumerable then serialize the result as a JSON string and send it to the client.
If I'm correct, the performance hit is quite heavy when you hit the couple thousand of entries (which will happen quite fast once in production... everytime an employe comes to work, it will add 1 entry. 100 employes, ~300 work days a year, you get the idea).
The reason for this hack is that the EF does not know what "getEmploye" method of "Poincon" is, therefor throwing an exception at runtime similar to this:
LINQ to Entities ne reconnaît pas la méthode « PortailNorclair.Models.Employe getEmploye() », et cette dernière ne peut pas être traduite en expression de magasin.
Approximated traduction (If anyone can let me know in a comment how to configure IIS / ASP.NET to display errors in english while keeping the globalization in a foreign language, I would be really grateful. French information about error messages is sometimes lacking):
LINQ to Entity does not recognize the method " PortailNorclair.Models.Employe getEmploye()" and the following could not be translated to a SQL expression.
The "getEmploye" method instances and returns a Employe object with the employe id found in the Poincon object. That Employe object has properties that "lazy loads" information like the employe name from the Active Directory.
So the question is: How can I avoid the performance hit from using .AsEnumerable() on the non-filtered list of objects?
Thanks a lot!
The "getEmploye" method instances and returns a Employe object with
the employe id found in the Poincon object. That Employe object has
properties that "lazy loads" information like the employe name from
the Active Directory.
You should be storing the Employee Name in the database, so you can then order, sort, skip and take in your Linq Query without having to load every employee object.
If empNoStr, empNom, and empNo were all in the database, you could retrieve just the records you want, and call getEmploye() (loading whatever else you need from active directory, or wherever) for each of those.
There are some classes on which your program performs its main work.
There are other classes which represent to database rows.
If you keep them separated, you can also separate actions you intend to occur in the database from actions you intend to perform locally. This makes it trivial to avoid loading the full table, when specific rows are required.
I see you're also doing Paging locally, while the database can do that and save your webserver some memory.
I'm going crazy with this and would greatly appreciate some help.
Imagine two tables connected with a foreign key:
Fonts
FontColors
I need to get a computer FontColors report with the Fonts table info included as well. Via the entity framework I can obviously access font colors "Fonts" table properties the following way:
string fontName = FontColors.Fonts.Name;
Simple...right?
Now imagine I created another class called ComputerFontColors, for example, to match my report model that I'll display (Jquery Grid report by the way ) that will include that same Font and FontColors information in addition to some info pulled form my Computer description table.
So to populate that ComputerFontColors class we go with something like this:
var computerFonts = from f in FontColors
select new ComputerFontColors
{
FontColor = f.Color,
FontName = f.Fonts.Name,
ComputerUsedOn = ComputerServices.GetByFontId(f.Fonts.Id)
}
Seems to be as simple as it can get but for whatever reason it just doesn't work. Nhibernate Linq doesn't like the "ComputerUsedOn = ComputerServices.GetByFontId(f.Fonts.Id)" part and just keeps coming back with with "Could not instantiate: FontFolors " error.
The method
ComputerServices.GetByFontId(f.Fonts.Id)
by itself works fine.
The query with a static value thrown in:
var computerFonts = from f in FontColors
select new ComputerFontColors
{
FontColor = f.Color,
FontName = f.Fonts.Name,
ComputerUsedOn = ComputerServices.GetByFontId(6)
}
works fine.
But when I combine them together - code crashes.
Been stuck on this one.
Thanks in advance.
For kicks, try this:
var computerFonts = FontColors.AsEnumerable().Select(f =>
new ComputerFontColors
{
FontColor = f.Color,
FontName = f.Fonts.Name,
ComputerUsedOn = ComputerServices.GetByFontId(f.Fonts.Id)
});
AsEnumerable forces the projection to be done using the standard Linq to Objects methods, i.e. in-memory. Could be that Linq to NH has trouble understanding that your projection can't be done entirely in the database (given that ComputerServices.GetByFontId is a method that can't be translated to a query).