Generic query with Entity Framework ORM - c#

So currently i am writing a specific SQL function to get a specific row from a specific table.
However, I have dozens of tables, and noticed that I am writing these same 'get row' repository functions each time I make a new table.
Is it possible to write a generic function that works for every table, in this case to get a specific row?
Current (Example)
public Purchase GetPurchase(long purchaseId)
{
using (var db = new DbContext(_connStringKey))
{
var result = (from p in db.Purchases
where p.PurchaseId.Equals(purchaseId)
select p).FirstOrDefault();
return result;
}
}
Generic Example (To give you an idea)
public Object GenericGet (string tableName, string rowName, long rowId)
{
using (var db = new DbContext(_connStringKey))
{
var result = (from p in db.tableName
where p.rowName.Equals(rowId)
select p).FirstOrDefault();
return result;
}
}

You can do it using reflection but it is not a good approach. Instead of this, you could try something using the generics aspects of the language, and make sure what you want, for sample:
public T Get<T>(Expression<Func<T, bool>> filter)
where T : class
{
T result;
using (var db = new DbContext(_connStringKey))
{
result = db.Set<T>().FirstOrDefault(filter);
}
return result;
}
Remember that the T must be a reference type, so, define a constraint for class
Then, you could try this:
var product = Get<Product>(p => p.Name == "Something");
var supplier = Get<Supplier>(p => p.Sector == "Manufacturing");

Related

How to Cast a IQueryable/IEnumerable result to a custom Model C# EntityFramework

Lets say that I have a Repository with this function
public async Task<IEnumerable<Contacts>> GetAll()
{
return await _context.Contacts.ToListAsync();
}
Where the Contacts Entity is the same one returning the call. But I didn't want to use the same class because there's some fields that I like to keep out of the call. There's any way that I could "mirror" a second model called "ContactsModel" to return the data without using Anonymous calls like :
var result = context.t_validation.Where(a => a.isvalidated == 10).Select(x => new
{
x.date_released,
x.utoken,
x.Images,
x.images_key,
x.Type
});
Of putting into a loop and passing to this new Model :
foreach (var item in list)
{
decp.Add(new ValidationModel
{
uToken = item.utoken,
Date = item.date_released,
Images = bc.Decrypt(item.Images, item.images_key),
Type = item.Type
});
}
Thanks!
Because you are using custom method to decrypt an image, you will not be able to include it in the query, because EF will not be able to translate it into sql query.
Anonymous approach would be the best one
public async Task<IEnumerable<Contacts>> GetAll()
{
var models = await _context
.Contacts
.Select(contact => new
{
contact.date_released,
contact.utoken,
contact.Images,
contact.images_key,
contact.Type
})
.ToListAsync()
return models
.Select(item => new ValidationModel
{
uToken = item.utoken,
Date = item.date_released,
Images = bc.Decrypt(item.Images, item.images_key),
Type = item.Type
}
.ToList();
}
Of course you can wrap it with an extension methods, but if you are using this mapping only in one place you don't need to.

Join or Mutible DBContext Repository Generic c#

I have repository generic where I do method as Get,Update,Insert.
I get a data from table in data base I use this method.
public IEnumerable<typeEntity> Get<typeEntity>(Expression<Func<typeEntity, bool>> newObjectEntity,int page, int rowsByPage) where typeEntity : class
{
List<typeEntity> Result = null;
Result = Context.Set<typeEntity>().Where(newObjectEntity).OrderBy(m => true).Skip<typeEntity>(5 * (page - 1)).Take<typeEntity>(rowsByPage).ToList<typeEntity>();
return Result;
}
I when get data only a one table this is my code:
var collecProducts = repository.Get<Products>(c => true);
My problem is when I want get two tablet How I do this?. I find this code but is very slow.
var collecProducts = repository.Get<Products>(c => true);
var collecCategory = repository.Get<Category>(c => true);
var collectProductToCategory = (from p in collecProducts
join c in collecCategory on p.idCategory equals c.idCategory).ToList();
The problem this code is that get all data de products and category and I want from SQL Server only data necessary for example as join TSQL.
select p.idProducts from products p join category c on p.idCategory = c.idCategory
In conclusion How I could get data use join since repository generyc.
The solution you found is slow because the repository method is materializing/executing the query immediately instead of allowing deferred execution to occur. Try removing the ".ToList()" from the query within the repository method:
public IEnumerable<typeEntity> Get<typeEntity>(Expression<Func<typeEntity, bool>> newObjectEntity,int page, int rowsByPage) where typeEntity : class
{
IEnumerable<typeEntity> Result = null;
Result = Context.Set<typeEntity>().Where(newObjectEntity).OrderBy(m => true).Skip<typeEntity>(5 * (page - 1)).Take<typeEntity>(rowsByPage);
return Result;
}
This will allow you to compose and construct higher order queries without pulling the whole table into memory immediately.
I find a form get this result for example Product and Category in repository Generic through include.
public IEnumerable<typeEntity> Get<typeEntity>(Expression<Func<typeEntity, bool>> newObjectEntity,int page, int rowsByPage, string include) where typeEntity : class
{
List<typeEntity> Result = null;
Result = Context.Set<typeEntity>().Where(newObjectEntity).include(include).OrderBy(m => true).Skip<typeEntity>(5 * (page - 1)).Take<typeEntity>(rowsByPage).ToList<typeEntity>();
return Result;
}
With include you can string for example Product->Category and get this object.

Retrieving data from database using conditional query concatenation as in SQL query

I have a class which is used to carry filtering data. I want to retrieve data from database based on the filtering options.
My FilteringDto class:
public class FilteringDto
{
public int id { get; set; }
public string search_text { get; set; }
}
I want to retrieve data from CafeTableGroup table. This is what my query looks like:
using (ISession session = SessionFactory.OpenSession)
{
using (ITransaction transaction = session.BeginTransaction())
{
groups = session.CreateCriteria<CafeTableGroup>().List<CafeTableGroup>();
if (string.IsNullOrEmpty(filters.search_text))
{
groups = groups.Where(a => a.field_1.Like(filters.search_text)).ToList();
}
if (filters.id != 0)
{
groups = groups.Where(a => a.field_2== filters.id).ToList();
}
transaction.Commit();
}
}
But I have a problem here. In order to get filtered data, first it retrieves all the data in table and then filters it based on the condition. Is there any way that I can do it using a single query and retrieve only the filtered data not all the datas? Thanks in advance.
Problem in your code is .List<CafeTableGroup>(); which is causing materializing the instances too early. Just delay the call to List.
I am not using your exact example. Also, my code uses IQueryOver instead of CreateCriteria. You can achieve this with code something like below:
public IList<Table1Entity> GetList(FilterParams filterParams = null, PageParams pageParams = null)
{
IList<Table1Entity> instance = null;
Conjunction conjTable1 = Restrictions.Conjunction();
Conjunction conjTable2 = Restrictions.Conjunction();
if(filterParams == null)
filterParams = new FilterParams();
if(!string.IsNullOrEmpty(filterParams.Date))
conjTable1.Add(Restrictions.Eq(Projections.Property<Table1Entity>(x => x.Date), filterParams.Date));
if(!string.IsNullOrEmpty(filterParams.FromTime))
conjTable1.Add(Restrictions.Eq(Projections.Property<Table1Entity>(x => x.FromTime), filterParams.FromTime));
if(!string.IsNullOrEmpty(filterParams.ToTime))
conjTable1.Add(Restrictions.Eq(Projections.Property<Table1Entity>(x => x.ToTime), filterParams.ToTime));
if(!string.IsNullOrEmpty(filterParams.Id))
conjTable1.Add(Restrictions.Eq(Projections.Property<Table1Entity>(x => x.Id), Guid.Parse(filterParams.Id)));
if(!string.IsNullOrEmpty(filterParams.Pid))
conjTable2.Add(Restrictions.Eq(Projections.Property<Table2Entity>(x => x.Pid), Guid.Parse(filterParams.Pid)));
IQueryOver<Table1Entity> query = NHSession.QueryOver<Table1Entity>()
.Where(conjTable1)
.JoinQueryOver(x => x.Table2)
.And(conjTable2);
if(pageParams != null)
query = query.Skip(pageParams.SkipRecords).Take(pageParams.TakeRecords);
instance = query.List();
return instance;
}
This also demonstrates how to implement joins and paging.

Simple LINQ Query with LINQ to SQL, think I'm doing something wrong

I have a table in my LINQ to SQL portion of my project.
I'm just trying to perform a simple query like so:
public static string GetMLBID(int fk_players_id)
{
using (MLBDataClassesDataContext context = new MLBDataClassesDataContext())
{
var query = from a in context.players
where a.fk_player_type_id == fk_players_id
select a.mlb_com_id;
foreach (var b in query)
{
Console.WriteLine(b.); //<-- I don't get the properties listed in the "players" table that i linked in the imgur link.
}
}
}
From all the examples in google.. where i have "b.", the properties from the table i have should be popping up.. but that's not listed. i only get simple LINQ operators and methods.
I feel like i'm missing something really simple.. any help?
You are only selecting the id mlb_com_id
select a.mlb_com_id;
Change the select clause to
select a;
This allows you to access all the public members of a on the result set.
EDIT by Pellared. (The point of Pellared's addition is that the extension method syntax does not require a Select-clause and would therefore not have lead to the error):
You can also change the query using lamba expressions (which a lot of people prefer)
public static string GetMLBID(int fk_players_id)
{
using (MLBDataClassesDataContext context = new MLBDataClassesDataContext())
{
var query = context.players
.Where(a => a.fk_player_type_id == fk_players_id);
foreach (var b in query)
{
Console.WriteLine(b.mlb_com_id);
}
}
}
You need to get your elements as a list and you should be able to access the properties then.
public static string GetMLBID(int fk_players_id) {
using (MLBDataClassesDataContext context = new MLBDataClassesDataContext())
{
var query = (from a in context.players
where a.fk_player_type_id == fk_players_id
select a).ToList();
foreach (var b in query)
{
Console.WriteLine(b.first_name);
}
}
}

LINQ query for multiple databases c#

I have not found any good material for querying multiple databases using LINQ. I have been using connection strings to change between databases and display data for user, now i want to implement a search function which query's all databases and returns a list instead of selecting the database before hand.
Here's what I've thrown together so far. which returns one list from one database which obviously is not what i want.
public ActionResult getCustomers(string cust)
{
List<trakman_Entities> teInstances = new List<trakman_Entities>();
IEnumerable<customer> customers = null;
for (var i = 1; i < ConfigurationManager.ConnectionStrings.Count; i++)
{
if (ConfigurationManager.ConnectionStrings[i].ConnectionString.ToLower().Contains("metadata"))
{
string con = ConfigurationManager.ConnectionStrings[i].ConnectionString;
teInstances.Add(new trakman_Entities(con));
}
}
foreach (trakman_Entities entitiy in teInstances)
{
customers = entitiy.customers.Where(c => c.code.StartsWith(cust));
}
foreach(customer c in customers)
{
Response.Write(c.code);
Response.Write(c.name);
}
Response.End();
return View(customers);
}
The problem is that you keep reassigning the customers variable in your foreach loop:
foreach (trakman_Entities entitiy in teInstances)
{
// overwrites on each iteration!
customers = entitiy.customers.Where(c => c.code.StartsWith(cust));
}
Instead, consider:
var customers = teInstances.SelectMany(e => e.customers.Where(c => c.code.StartsWith(cust)))
.ToList();
Or, do do the whole thing using a single LINQ query:
// start with the list of connection string settings cast to IEnumerable<T>
var customers = ConfigurationManager.ConnectionStrings.Cast<ConnectionStringSettings>()
// filter to the relevant connection strings
.Where(s => s.ConnectionString.ToLower().Contains("metadata"))
.SelectMany(s => {
// for each connection string, select a data context
using( var context = new trakman_entities(s.ConnectionString)) {
// for each context, select all relevant customers
return context.customers.Where(c => c.code.StartsWith(cust)).ToArray();
} // and dispose of the context when we're done with it
})
.ToList();

Categories