Recently I had to add a new column for a shop I've made, determining whether item should be available for sale or not.
Now the thing. Is it possible to do something like global where clause or I must to add it separately for each query, that relates to certain column (e.g. Products)? It would be really hard to correct every query and not miss anything.
Example query I use looks like this, but it's just a very basic one. Normally these where clasues are multiline, including selects from another tables.
DataBaseContext db = new DataBaseContext();
// ...
protected bool SomeFunction() {
// ...
var Products = db.Products.
Where(k => k.Active == true).
OrderByDescending(k => k.Date);
// ...
}
Normally I would do
var Products = db.Products.Where(k => k.Active == true);
Products = Products.
Where( ... ).
Select( ... ).
OrderBy( ... ).
...
Take( ... );
But there are mulptiple functions (db is common for every function in class), and I was thinking about writing the condition on the SQL server side, but I have no knowledge about that, sadly.
A simple solution would be to change your products implementation:
Old:
class DataBaseContext
{
//...
public DbSet<Product> Products { get; set; }
}
New:
class DataBaseContext
{
//...
public IQueryable<Product> Products
{
get
{
return this.Set<Product>().Where(pr => pr.IsActive == true);
}
}
}
However, this is not very robust and maintenance friendly, since you would have to do this for every type of item that can be activated. Also, you would need to create a second property of type DbSet called AllProducts and then vary between if you want to get active or allitems by checking all the points where the query is used.
Alternatively, you could create a wrapper for your DbContext:
interface IMyContext {
void SaveChanges();
IQueryable<T> Set<T>() where T: class
IQUeryable<T> GetActiveItems<T>() where T : SomeBaseClassWithActiveProperty
}
public class MyContext : IMyContext {
DataBaseContext _underylingContext = new DataBaseContext();
//... save changes implementation etc
public IQueryable<T> Set<T>()
where T : class
{
return _underlyingContext.Set<T>();
}
public IQueryable<T> GetActiveItems<T>()
where T : SomeBaseClassWithActiveProperty
{
return this.Set<T>().Where(item => item.IsActive == true);
}
}
then, when using it:
MyContext context = new MyContext();
var activeProducts = from p in context.GetActiveItems<Product>()
order p by p.Date //... or whatever;
var allProducts = from p in context.Set<Product>() //....
Either way, you should go by and check all calls to the Product DbSet and validate if you only need active items or all items.
You can do this inside the database with the following two steps:
(1) Rename your existing table to something else.
(2) Create a view with the name of the existing table:
create view <tablename> as
select *
from <newtablename>
where <your condition is true>;
(You probably want to list all the column instead of using *.)
Now all queries will use the view instead of the base table.
By the way, when designing an API for a database, it is a good idea to have all access be through views. This allows changes like this after the API is in place.
Related
I am trying to understand the performance impact of having one DbContext class vs multiple when using EF6 framework.
For example, if we have a simple DbContext such as this:
public class MainDbContext : DbContext
{
public DbSet<Car> Cars { get; set; }
public void AddCar(Car car)
{
Cars.Add(car);
SaveChanges();
}
}
Now let us say I have a service that uses the said DbContext in the following way:
public class CarService
{
public List<Car> Cars { get; private set; }
public CarService()
{
var dbContext = new MainDbContext();
Cars = dbContext.Cars.ToList();
}
}
At what point in time did the DbContext go to the database and retrieved all cars that are stored in the database? Was it when I called var dbContext = new MainDbContext(); or was it when I called Cars = dbContext.Cars.ToList();?
If former, if I had a DbContext that contains 100 tables, will all 100 tables be queried when the DbContext is created?
No. The query happens once you enumerate a table. Even in your second example, it still does not connect to the database.
It will connect when you enumerate, for example:
dbContext.Cars.ToList();
Or
foreach (Car c in dbContext.Cars)
Or bind the table to a UI control.
However when you make a where, order by, then by, join etc.. e.g.
var result = dbContext.Cars.Where(c => c.Id == 35).OrderBy(c => c.Name);
You will not connect to the database. You are only preparing the logical sequence of operations to translate into an SQL query.
AFTER THE QUESTION UPDATE
Now, your second example, with the ToList(), enumerate the results and connects to the database to get the data.
Here is what I want to do:
var user = db.User.First(conditions);
user.Book.First();
Here is currently how I have to do that.
var user = db.User.Include("Book").First(conditionsForUser);
user.Book.First();
The reason why I want to simplify this, is because I don't want to have to specify what is included every time I want to access a relationship. Seems very cumbersome.
e.g.: I would like to just be able to do the following, given I have previously retrieved a user:
user.Book.First()
user.Blog.First()
user.SomeOtherHasManyRelationship.Where(conditions)
Here is what I have so far:
public object RelationshipFor(string relationship)
{
using (var db = User.DbContext())
{
var relationshipType = TypeRepresentedBy(relationship); // unused for now, not sure if I need the type of the relationship
var myTable = ((ICollection)db.Send(RelationshipName)); // RelationshipName is "User" in this instance.
var meWithRelationship = myTable.Where(i => i.Send(IdColumn) == Id).Include(relationship); // currently, myTable doesn't know about 'Where' for some reason.
return meWithRelationship.Send(relationship);
}
}
And then how that would be used would be the following:
user.RelationshipFor("Book") // returns a list of books
I have some other logic in my code which abstracts that further which would allow me to do user.Book.First().
Hopefully I can get permission to open source a lot of this, as I'm modelling a lot of the api after ActiveRecord-style crud.
Note, that I'm using I set of extensions I made to help dealing with dynamicness less painful: https://github.com/NullVoxPopuli/csharp-extensions
UPDATE 1:
public object RelationshipFor(string relationship)
{
using (var db = User.DbContext())
{
var myTable = (DbSet<DatabaseModels.User>)db.Send(RelationshipName);
var myInclude = myTable.Include(i => i.Send(relationship));
var meWithRelationship = myInclude.First(i => (long)i.Send(IdColumn) == Id);
return meWithRelationship.Send(relationship);
}
}
For now, I've hard coded the cast of the user in an attempt to just get something working.
My error now is:
Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN' to type 'System.Linq.Expressions.MemberExpression'.
This is not a trivial problem, and there's no "one size fits all" approach. What you actually seem to be after is lazy loading, which was not included in EF7 for many reasons.
I don't know what the code you show is supposed to do, but one option would be to introduce a repository pattern, where you specify the "entities to include" at the collection level:
public class UserRepository
{
private readonly IQueryable<User> _dataSet;
public UserRepository(IQueryable<User> userDataSet)
{
_dataSet = userDataSet;
}
public IQueryable<User> Include()
{
return _dataSet.Include(u => u.Book)
.Include(u => u.Blog);
}
}
And you can move lots of the logic to a generic base class, leaving you with just the Include() method. You can for example work with strings as you show (or enums, or ...), to only select related entities to include:
public class GenericRepository
{
// ...
public IQueryable<User> Include(string includeGroup = null)
{
return IncludeGroup(includeGroup);
}
protected virtual IncludeGroup(string includeGroup)
{
return _dataSet;
}
}
And then in UserRepository:
protected override IQueryable<User> IncludeGroup(string includeGroup)
{
switch (includeGroup.ToUpperInvariant())
{
case "BOOK":
return _dataSet.Include(u => u.Book)
.Include(u => u.Book.Author);
case "BLOG":
return _dataSet.Include(u => u.Blog);
default:
return base.Include(includeGroup);
}
}
And then use it like this:
var userRepo = new UserRepository(db.User);
var userWithBooks = userRepo.Include("Book");
var firstUser = userWithBooks.FirstOrDefault(u => u.Name == "Foo");
var firstUserFirstBook = firstUser.Book.FirstOrDefault();
One alternative would be to always include all navigation properties (recursively), but that would be a horrible approach in terms of query efficiency, as every query will be one massive join to all related tables, whether that is necessary or not.
I am working on a program to manage my personal finances. I have an SQLite database that is storing all of the data, and I am able to load/save accounts, bills, payments, etc.
What I am looking to do, is load the account name of the associated account based on the PayeeId of each Payment. I know how to achieve this using SQL, but my data is set up using repositories. For example, I load the Payments by calling
var payments = await _paymentsRepository.LoadAllAsync();
And the LoadAllAsync() method is in RepositoryBase class, and looks like so:
public async Task<IEnumerable<TTable>> LoadAllAsync()
{
var query = _sqliteService.Conn.Table<TTable>();
var array = (await query.ToListAsync()).ToArray();
return array;
}
and is declared in the IPaymentsRepository interface like so:
Task<IEnumerable<Payment>> LoadAllAsync();
Each Payment object has a PayeeId property that links to the Payee for that Payment. The Payment itself doesn't store any of the other information about the Payee, but I would like to be able to load the PayeeName property to display with the Payment information. Is there a simple way to do this, or will I have to create a separate ViewModel to store the "hybrid" data containing both the Payment information as well as the Payee information?
EDIT
I know I can accomplish this using an extra class, for example PaymentInfo or something, and store both the Payment and Payee data, then access it like this: PaymentInfo.Payment.PaymentAmount or PaymentInfo.Payee.PayeeName, but I would have to load them in two separate queries. While this is certainly possible, I am hoping for a solution that can be accomplished in one query, which is why I am looking at using a JOIN. If I need to, I will just use LINQ, but my question is whether or not this is possible using the repository setup that I currently have.
EDIT 2
Here is the repository code. I have tried to only include the relevant pieces. Each table has its own repository. Here is the signature of the PaymentsRepository:
public class PaymentsRepository : RepositoryBase<Payment, int>, IPaymentsRepository
{
}
The RepositoryBase<> looks like this:
public abstract class RepositoryBase<TTable, TKey> : IRepository<TTable, TKey>
where TTable : IKeyedTable<TKey>, new()
{
protected readonly ISqliteService SqliteService;
protected RepositoryBase(ISqliteService sqlLiteService)
{
SqliteService = sqlLiteService;
}
public async Task<IEnumerable<TTable>> LoadAllAsync()
{
var query = SqliteService.Conn.Table<TTable>();
var array = (await query.ToListAsync()).ToArray();
return array;
}
......
}
The IRepository interface:
interface IRepository<TTable, in TKey>
where TTable : IKeyedTable<TKey>, new()
{
Task<TTable> LoadByIdAsync(TKey id);
Task<IEnumerable<TTable>> LoadAllAsync();
Task InsertAsync(TTable item);
Task UpdateAsync(TTable item);
Task DeleteAsync(TTable item);
AsyncTableQuery<TTable> Query();
}
And the ISqliteService:
public interface ISqliteService
{
SQLiteAsyncConnection Conn { get; }
Task<object> ClearLocalDb();
void Reconnect();
}
Everything is ultimately queried against that SQLiteAsyncConnection property, using the built-in SQLite methods. For example, in the LoadAllAsync() function, var query = _sqliteService.Conn.Table<TTable>(); uses this:
public AsyncTableQuery<T> Table<T> ()
where T : new ()
{
//
// This isn't async as the underlying connection doesn't go out to the database
// until the query is performed. The Async methods are on the query iteself.
//
var conn = GetConnection ();
return new AsyncTableQuery<T> (conn.Table<T> ());
}
which is located in SQLiteAsync.cs
I wasn't able to figure out a way to directly query two different tables using LINQ, but I got things working with a "hybrid" class. I just created a PaymentInfo class that has a Payment property and a Payee property, which point to the relevant data. I added a method to my PaymentsRepository that looks like this:
public async Task<IEnumerable<PaymentInfo>> LoadAllPaymentInfoAsync()
{
var payments = await SqliteService.Conn.Table<Payment>().ToListAsync();
var payees = await SqliteService.Conn.Table<Payee>().ToListAsync();
var query = from p1 in payments
join p2 in payees on p1.PayeeId equals p2.Id
select new PaymentInfo() {Payment = p1, Payee = p2};
return query;
}
I am sure that this is not necessarily the best way to accomplish this, but I thought I would share it here in case anyone comes across this page looking to do what I did.
I think you can get IQueryable<Payment> and IQueryable<Payee>, join them in LINQ, and then call .ToArray() on the result.
It will build up the query and perform it only when you actually access the data (in this case, on the ToArray() call). I believe this should generate a single query.
For work, we have specific types of records that come in, but each project has its own implementation. The columns and the like are different, but in general the process is the same (records are broken into batches, batches are assigned, batches are completed, batches are returned, batches are sent out, etc.). Many of the columns are common, too, but sometimes there are name changes (BatchId in one vs Id in another. [Column("name")] takes care of this issue).
Currently this is what I have for the implementation of the batch assignment functionality with the common components given in the interface:
public interface IAssignment
{
// properties
...
// methods
T GetAssignmentRecord<T>(int UserId, int BatchId) where T : IAssignment;
List<T> GetAssignmentRecords<T>(int UserId) where T : IAssignment;
}
Now I currently have two projects that have batch assignment. Due to these being done in EntityFramework, Assignment in Namespace1 and Assignment in Namespace2 are completely different things but are bound by certain common components (an ID, an assigned user, checked in, etc.) which drive all of the methods for returning them.
I think my main question is if I'm doing this incorrectly and if there is a better way to achieve this such that I can pipe data into my Controllers and have the controllers look somewhat similar project to project while having as much of the method work being handled automatically (primarily so that a "fix one, fix all" scenario occurs when I need to do updates).
Here's an example of how I'm doing the implementation for namespace1:
public class Assignment
{
...
public T GetAssignmentRecord<T>(int UserId, int BatchId) where T : IAssignment
{
var db = new Database1Context();
return (T) Convert.ChangeType(db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned).First(), typeof(T));
}
}
In the Controller:
Assignment assignment = new Assignment();
var record = assignment.GetAssignmentRecord<Assignment>(userid, batchid);
// do stuff
The controller code is actually how I'm assuming it would work. I've completed through the Assignment class and now I'm perplexed if I'm doing it the proper way. The reason I feel this may be incorrect is I'm basically saying "The interface is looking for a generic, I'm getting a strong typed object from the database using entity framework, I'm casting it to a generic, and when I'm making the request, I'm asking for the same strong typed object that I converted to generic initially."
Is there a better way of doing this? Or a completely different direction I should be going?
Providing I understood correctly what your goal is, I'd do it e.g. this way...
interface IAssignment
{
}
interface IRepo<out T> where T : IAssignment
{
T GetAssignmentRecord(int UserId, int BatchId);
IEnumerable<T> GetAssignmentRecords(int UserId);
}
class AssignmentRecord : IAssignment
{
}
class AssignmentWeb : IAssignment
{
}
class RepoDb : IRepo<AssignmentRecord>
{
public AssignmentRecord GetAssignmentRecord(int UserId, int BatchId)
{
//using(var db = new MyDbContext())
//{
// return db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned).First();
//}
return new AssignmentRecord();
}
public IEnumerable<AssignmentRecord> GetAssignmentRecords(int UserId)
{
//using(var db = new MyDbContext())
//{
// return db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned);
//}
return new List<AssignmentRecord>
{
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
new AssignmentRecord(),
};
}
}
class RepoWeb : IRepo<AssignmentWeb>
{
public AssignmentWeb GetAssignmentRecord(int UserId, int BatchId)
{
// fetch it from some web service...
return new AssignmentWeb();
}
public IEnumerable<AssignmentWeb> GetAssignmentRecords(int UserId)
{
//using(var db = new MyDbContext())
//{
// return db.Assignment.Where(c => c.UserId == UserId && c.BatchId == BatchId && c.Assigned);
//}
return new List<AssignmentWeb>
{
new AssignmentWeb(),
new AssignmentWeb(),
new AssignmentWeb(),
};
}
}
class MYController
{
public IRepo<IAssignment> Repository { get; set; } // you can inject this e.g. DI
public IAssignment GetAssignment(int userid, int batchid)
{
return Repository.GetAssignmentRecord(userid, batchid);
}
public IEnumerable<IAssignment> GetAllAssignments(int userid)
{
return Repository.GetAssignmentRecords(userid);
}
}
class ProgramAssignment
{
static void Main(string[] args)
{
try
{
var controller = new MYController();
controller.Repository = new RepoDb();
IAssignment assignment = controller.GetAssignment(0, 0);
IEnumerable<IAssignment> all = controller.GetAllAssignments(0);
controller.Repository = new RepoWeb();
assignment = controller.GetAssignment(0, 0);
all = controller.GetAllAssignments(0);
}
catch
{
Console.WriteLine("");
}
}
}
As to why the out - here is some more in my other post...
How to make generic class that contains a Set of only its own type or subtypes as Children?
Assuming that the 2 Assignment has different properties (maybe some additional), but some of the property is same, and they are from different database, there are many ways to doing it. But "the best" (for me) is by doing dependency injection.
Your activities (methods) in Assignment class, should be moved to a separated "service" class. This increases the modularity of Assignment, as it only became a POCO.
For data access, create a separated class (repository) to retrieve/insert/update/delete your data. Example will be like:
public AssignmentRepository: IAssignmentRepository{
public Assignment GetAssignmentRecord(int userId, int batchId){
}
}
public BatchAssignmentRepository: IAssignmentRepository{
public Assignment GetAssignmentRecord(int userId, int batchId){
}
}
If you ask why there are 2 repository instead of 1, will it make the code redundant? Yes it is, but you also must consider it will increase the modularity. If you change something in BatchAssignment (maybe change the column name, add additional column, etc) then you do not need to apply the same in Assignment, and avoiding you of "if batchAssignment else" logic inside.
The use from the caller will be like this:
IAssignmentService service = new AssignmentService();
IAssignmentRepository repository = new AssignmentRepository();
Assignment a = repository.GetAssignmentRecord(userId, batchId);
service.DoSomething(a);
Think about an adapter layer. That layer should transform the incoming data to a common structure/class and then can be handled consistently, generics notwithstanding. Of course it also re-transforms on the "outbound" side to that expected by the particular databases. This assumes that no datasource has data that is undefined in the others, or that you can define valid default values for said missing data.
I imagine you need different adapters for the different projects. Perhaps this is a job for dependency injection. Basically at runtime you fetch the particular code (adapter class) needed.
Introduction to Unity.
I have a problem in architecting my application.
I have the following structure with only important aspects shown.
namespace Domain
{
public class Invoice
{
//properties
}
public class InvoiceRepository
{
public Linq2SqlContext context = new Linq2SqlContext();
public IQueryable<Invoice> GetInvoices()
{
var query = from inv in _dbctx.Invoices orderby inv.invInvoiceDate descending select GetInvoice(inv) ;
return query;
}
}
public class InvoiceService()
{
public InvoiceRepository _repository = new InvoiceRepositroy();
public IQueryable<Invoice> GetInvoices()
{
return _repository.GetInvoices();
}
}
}
namespace MyApp
{
public class UI
{
public InvoiceService _service = new InvoiceService();
public void FilterInvoices()
{
var query =
(
from i in _service.GetInvoices()
from s in _service.GetStatuses()
where i.ProjectID == _projectid &&
s.ID == i.Status
select new
{
InvoiceID = i.ID,
DocumentTotal = i.TotalDue.ToString(),
Created = i.Created,
WeekEnding = i.WeekEnding,
Status = s.staStatus
}
).Skip(_pageIndex * _pageSize).Take(_pageSize);
}
}
{
So I want to return IQueryable from my service so I can
filter from client code. But the problem I'm coming up with
is the FilterInvoices method errors with "No supported
translation to sql" because of the GetInvoice method
which is iused to return an Invoice entity (this is
a layer on top op the LInq2 sql layer) and not an Linq2sql Invoice entity.
So how do I return a IQueryable from my service with this structure??
Also how do I sort and return a IQureyable in repository GetInvoices.
Hope this makes sense.
Malcolm
linq2sql thinks GetInvoice (within GetInvoices) is a stored procedure. One way around it
var query = from inv in _dbctx.Invoices orderby inv.invInvoiceDate descending select inv ;
though that would pass back the objects generated by your datacontext. If you wanted to populated custom objects you could iterated over the collection creating your custom Invoice objects and populating them.
foreach(var inv in query) { somelist.Add(new MyCustomInvoince() { id = inv.id ... }
EDIT: The above will return a list. Use the following to return IQueryable
return from item in query
select GetInvoice(item);
The difference is at this stage your are using Linq2Objects, and that provider will know how to call GetInvoice
You cannot query with LTS (Linq to SQL) something built 'on top' of the LTS layer.
The reason is that the LTS entities layer is a mapping of the content of you database, and the query you perform is 'just' translated into SQL.
I personnaly use another approach to keep an independance between my layers...
I create interfaces that match my LTS entites, and I use the Cast<>() method to have my repository return the interface instead of the concrete implementation.
It works perfectly.
You need to extend the base entity (no pb as it is a partial class) :
partial class Employee : IEmployee
And you need this property in your repository :
public IQueryable<IEmployee> Query
{
get
{
return this._context.Employees.Cast<IEmployee>();
}
}
Based on this, you could code a generic repository, but it's another story (more complicated)
This is just off the top of my head, but you could try:
from i in _service.GetInvoices().AsEnumerable()