Override DbContext when returning IQueryable - c#

In my database, all my tables have 5 repeating fields (CreateUser, UpdateUser, IsRemoved...)
Now, I call the DbContext normally as:
mydbEntities db = new mydbEntities();
And then when I need to bring up all the students and all classes I call them normally as:
var students = db.students;
var classes = db.classes;
BUT! Since I have to NOT bring the ones that have column IsRemoved == true , I'm rewriting everywhere in my app to:
var students = db.students.Where(m => m.IsRemoved == false);
var classes = db.classes.Where(m => m.IsRemoved == false);
To avoid bugs and errors when replacing existing code, when the scaffolding creates new controllers, and other developers forgetting to add the Where(), makes me wonder if I can overwrite the call db.students so that internally, it ALWAYS adds the Where()
This will also help in the future, so when I call db.students.Include(s => s.classes) that both times the IsRemoved is taken into account and even if the student exists, if the class has been removed, then it won't return it in the list.

This is why I believe that having Repository classes - for the database access layer - is always a good idea. So you never have to access your dbContext object directly, but through a layer of abstraction, eg. the StudentRepositoy.
IStudentRepository studentRepository = new StudentRepository(dbContext);
var students = studentRepository.getStudents();
In this case, you only need to add the Where(m => m.IsRemoved == false) once, inside your getStudents() method.
I hope you don't have those queries repeated in your controllers (I don't know if you are using MVC or some other thing). Check out this tutorial for more detail information.
Although, this does not answer your question, I hope you consider these modifications.
Edit:
It does look like there is a way to override dbcontext from the documentation

Have you considered creating a view in the database that accounts for the IsRemoved flag?
Select * From <tablename> Where IsRemoved = 0
Then add the view to your DBContext.
Just an idea.

Related

Is there a way to automatically create CRUD for EF Model (DB First currently)

I am creating a WPF app and I have an existing DB that I would like to use and NOT recreate. I will if I have to, but I would rather not. The DB Is sqlite and when I add it to my data later and create a DataModel based on the DB, I get the model and the DB Context, however there are no methods created for CRUD or for instance .ToList() so I can return all of the items on the table.
Do I need to create all of these manually or is there a way to do it like the way that MVC can scaffold?
I am using VS 2017, WPF, EF6 and Sqlite installed with Nu-Get
To answer the question in the title.
No.
There is no click-a-button method of scaffolding out UI like you get with MVC.
If you just deal with a table at a time then you could build a generic repository that returns a List for a given table. That won't save you much coding, but you could do it.
If you made that return an iQueryable rather than just a List then you could "chain" such a query. Linq queries aren't turned into SQL until you force iteration and you can base one on another adding criteria, what to select etc etc for flexibility.
In the body of your post you ask about methods to read and write data. This seems to be almost totally unrelated from the other question because it's data access rather than UI.
"there are no methods created for CRUD or for instance .ToList() so I can return all of the items on the table."
There are methods available in the form of LINQ extension methods.
ToList() is one of these, except it is usual to use async await and ToListAsync.
Where and Select are other extension methods.
You would be writing any model layer that exposed the results of those though.
I'm not clear whether you are just unaware of linq or what, but here's an example query.
var customers = await (from c in db.Customers
orderby c.CustomerName
select c)
.Include(x => x.Orders) //.Include("Orders") alternate syntax
.ToListAsync();
EF uses "lazy loading" of related entities, that Include makes it read the Orders for each customer.
Entity Framework is an Object Relational Mapper
Which means it will Map your C# objects to Tables.
Whenever you are creating a model from bd it will create a Context Class which will in inherit the DbContext. in this class you will find all the tables in DbSet<Tablename> Tablename{get; set;}. Basically, this list contains will the rows. the operation performed on this list will affect the DB on SaveChange method.
Example for CURD
public DbSet<Student> Students { get; set; }
//Create
using (var context = new YourDataContext()) {
var std = new Student()
{
Name = "Aviansh"
};
context.Students.Add(std);
context.SaveChanges();
}//Basically saving it will add a row in student table with name field as avinash
//Delete
using (var context = new YourDataContext()) {
var CurrentStudent=context.Students.FirstOrDefault(x=>x.Name=="Avinash")
CurrentStudent.context.Students.Remove(CurrentStudent);
context.SaveChanges();
}
Note: on SaveChanges the change will reflect on Db

How to pass DbContext (EntityFramework SqlConnection) to another Method?

Issue in passing DbContext to another method i.e. for e.g:
public bool MarkCustomerForDelete(Customer customerObj)
{
using(var dbContext = new MyContext())
{
using(var dbTransaction = dbContext.Database.BeginTransaction())
{
//Clear all orders for the Given Customers
var orderList = dbContext.Orders.Where(x=>x.id == customerObj.OrderId).ToList();
CommonLogicMethod(dbContext, orderList);
//Logic
customerObj.Status = "Deleted";
// The Modification will fail over due to the Customer Object for that object is already attached to the DbContext with Previous Values
dbContext.Entry(customerObj).State = EntityState.Modified;
dbContext.SaveChanges();
dbTransaction.Commit()
return true;
}
}
}
public void DeleteOrderRelatedData(MyContext dbContext, List<Orders> orderList)
{
foreach(var entity2 in entity2List)
{
var OrderAddresses = dbContext.OrderAddresses.Where(x=>x.Id == entity2.Id).ToList();
//Now if here the dbContext has 100 Entities (Tables)
//It internally Enumerates all the entities in the Local cache i.e. dbContext.Coupons.Local has all the Records from the DB in the Local present.
}
}
Question: Why does when DbContext is passed to another method internally calls for all the data i.e. in dbContext.Customers.Local has all the Data in the Database in First-Level Cache ?
Question: How to Pass DbContext from one Method to Another (without creating above given issue) ?
This is Creating problem related to modification of the Data i.e. DeleteCustomer will fail over.
Now, if the code in the DeleteOrderRelatedData, is merged into the DeleteCustomer function, it works fine.
I added a Logs for the dbContext , and dbContext while passing it to the Function internally is calling all the Select queries related to the different Queries..
For more details, please check this Video out : Link
Tools being used :
Entity Framework 6.0
System.Data.Sqlite
PostSharp for MethodBoundary Aspect.
Sounds like your problem is something to do with cascading deletions but the wording is difficult to understand ...
The statement in your question ...
DbContext is passed to another method internally calls for all the
data
... DbContexts don't just "go and get all data" automatically, you must be triggering something that's causing it.
It sounds to me like when you are deleting your customer object EF you are manually implementing the code for a cascading delete when what you should perhaps do is just add that to the model and then remove the customer object negating the need for all this extra logic.
In other words you have said / are trying to say "when a customer is deleted, also find and remove the customers related orders".
In the code sample above you do ...
//Clear all orders for the Given Customers
var orderList = dbContext.Orders.Where(x=>x.id == customerObj.OrderId).ToList();
This is purely getting the orders by executing a "select * from orders where customerid = customer.Id"
then in the method you define below that ...
public void DeleteOrderRelatedData(MyContext dbContext, List<Orders> orderList)
... it looks like you then want to further delete all the addresses for the order. Although you don't appear to be calling that method in the sample above.
Instead you can do something like this to have EF worry about the children and grandchildren deletions for you all in the db ...
Entity Framework (EF) Code First Cascade Delete for One-to-Zero-or-One relationship
Cascading deletes with Entity Framework - Related entities deleted by EF
The Microsoft documentation for this is here ...
https://msdn.microsoft.com/en-gb/data/jj591620.aspx
EDIT:
My answer was based on what I knew EF would do out of the box, it seems that the actual problem was caused by a component not mentioned in the question, the problem was not about performing a heirarchy of actions as I had interpreted it was in fact about solving the issue of how another third party component was adversely affecting the EF behaviour.
In repsonse to the question:
How to Pass DbContext from one Method to Another (without creating above given issue) ?
... Just do it as passing a context between 2 methods will not in its own right cause the problem you were having.
...
It seems that answering this question correctly was impossible :(
Issue was due to :
I was using PostSharp, to Log the Traces using OnMethodBoundaryAspect.
Now this was using Arguments internally.
Since while Logging, it was serializing the Arguments, And this was creating the Problem.

Entity Framework Designer First get navigation property as Tasks

The Task pattern says that in order to be consistent everything has to be completely async or completely not async.
By using entity framework designer first I can achieve this quite easily
var course = await db.Courses.FindAsync(CourseID);
Courses is a DbSet generated by entity framework and therefore has all the Async methods.
The problem is that if I add a navigation property to that class, the latter is not a DbSet and it does not contain any async method definition.
As an example, if I add a navigation property to a Students table, it will be created as a virtual ICollection Students
which means I cannot use the async methods.
But what I do want is having entity framework to automatically generate a Task<> in order to be able to await even the navigation properties.
Is It possible? my goal is to achieve something like this:
var course = await db.Courses.FindAsync(CourseID);
var student = await course.Students.FindAsync(StudentID);
while at the moment my options are mixing async/notAsync code:
var course = await db.Courses.FindAsync(CourseID);
var student = course.Students.First(p => p.ID = StudentID);
or not using the navigation property at all:
var course = await db.Courses.FindAsync(CourseID);
var student = await db.Students.Where(p => p.CourseID == course.ID && p.ID == StudentsID).FirstAsync();
Can you suggest a solution that do not require code first?
EDIT
according to https://entityframework.codeplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.#AsyncLazyLoading what im searching for is called "Async Lazy Loading" and is not a feature available yet (and maybe it will never be).
It seems that you can either use Lazy Loading OR the async features, maybe I should just wrap the property in a Task by awaiting Task.Run(course.Students.First(p => p.ID = StudentID)) but I'm not sure it is a good idea.
In my opinion, lazy loading (which would in my opinion be the case where enumerating the navigation property would trigger a database access) is a bad access pattern, as it simply means that database access will happen at surprising places, which can make application performance difficult to predict.
All the solutions below use an import from System.Data.Entity.
Solution 1: Use eager loading with an Include
var course = await db.Courses.Include(c => c.Students).FirstOrDefaultAsync(c => c.ID == CourseID);
var student = course.Students.First(p => p.ID == StudentID);
Advantages:
One database access that is required to load all objects - and this solution scales very well if you want to retrieve more than one Course object at a time;
The Students navigation property is loaded and can be used freely;
Drawbacks:
There is always at least one database access;
The whole set of related Student objects is loaded even if you needed only one;
Solution 2: Use the LoadAsync method that exists on the concrete collection class;
This solution relies on the fact that lazy-loaded collections are from the EntityCollection<TEntity> class.
First, I would define an extension method:
public static async Task LoadAsync<T>(ICollection<T> collection)
where T : class
{
if (collection == null) throw new ArgumentNullException("collection");
var entityCollection = collection as System.Data.Entity.Core.Objects.DataClasses.EntityCollection<T>;
if (entityCollection == null || entityCollection.IsLoaded) return;
await entityCollection.LoadAsync(CancellationToken.None).ConfigureAwait(false);
}
Then you could write something like:
var course = await db.Courses.FindAsync(CourseID);
await course.Students.LoadAsync();
var student = course.Students.First(p => p.ID = StudentID);
Advantage:
There may be no database access at all if the objects are already loaded in the context;
The navigation property Students is guaranteed to be loaded;
Drawbacks:
Susceptible to the "N+1 queries" issue;
Both the Course and the set of related Student objects can grow stale, which may trigger concurrency issues down the road; (note that concurrency issues that affect a relationship are harder to resolve than concurrency issues that affect a single record)
Solution 3: Use the CreateSourceQuery method on the concrete class to load only the Student object that you want.
OK, doing that does not work, and actually is a pretty bad idea.
However, a solution with the same advantages/drawbacks can be written, but in another way:
var course = await db.Courses.FindAsync(CourseID);
var studentsQuery = from c in db.Courses
where c.ID == CourseID
from s in c.Students
select s;
var student = await studentsQuery.FirstAsync(p => p.ID = StudentID);
Advantage:
You only load the one Student object that you are going to use;
Drawbacks:
The Students navigation property is not loaded, meaning that it cannot be used without potentially triggering a database access;
The second line will always trigger a database access (susceptible to the "N+1 queries" issue, or even running the method times);
Solution 4: Eager loading, more selective: load both the course and the student that interest you in the initial LINQ query.
I am not 100% sure that that solution will work as written.
var query = from c in db.Courses
where c.ID == CourseID
select new { course = c, student = c.Students.First(p => p.ID == StudentID) };
var result = await query.FirstOrDefaultAsync();
var course = result.course;
var student = result.student;
Advantages:
Only one database access is required to retrieve both objects;
You only retrieve the objects that you are going to work on;
Drawbacks:
The Students navigation property is not loaded, meaning that it cannot be used without potentially triggering a database access;
** When to use which solution? **
If you need the navigation property to be filled (either because you know that you will make use of most of its elements, or because you want to pass the parent entity to another component that is allowed to make use of that property however it wants), then use solution 1 or 2;
If you do not need the navigation property to be filled, then use solution 4. Only use solution 3 if you categorically have the Course object already loaded;

How to keep customer data segregated

As a simplified example I have users, products and customers. Users are allowed access to certain products and to certain customers.
I'm using an edmx-file to map my SQL Server to my code and get the data using linq. A typical query might look something like this:
from prod in ctx.Products
join userProduct in ctx.UserProduct
on prod.Id equals userProduct.ProductId
join user in ctx.UserProfile
on userProduct.UserId equals user.Id
where user.UserName == username // <-- username is a method parameter
select new Product
{
Id = prod.Id,
DisplayText = prod.UserFriendlyText
}
Every time I need data from the database I must join towards the access rights table to EXCLUDE data the user does not have access to. This means that if someone (and it will happen eventually) forget to join towards the access table a user will see too much. Is there a way to INCLUDE data instead so that if I forget the access tables nothing is shown?
I've also been thinking about separating the different customers into different databases as their data will never be related to each other and it will be a small disaster if I leak data between customers. Leaking products between users from the same customer is bad but not as critical.
If it matters I'm in a C# MVC4 CQRS architecture with eventual consistency between the read and write side.
I've checked stack overflow for similar questions but all I could find was this unanswered one:
Access rules in CQRS read model
How about using the Repository pattern, and forcing your Dev's to use it to make calls to the Database? This will promote code reuse and improve the maintainability of the app.
Because a method will be called from the repository you can control the code that interacts with the database, and force consistency, that way you can make sure that the access table is always used, and used as you wish.
I have a similar problem in my database. 90% of my entities are "organisation dependent". My approach uses a generic base repository with methods like this:
public virtual T Find(int id)
{
T e = Context.Set<T>().Find(id);
var od = e as OrganisationDependent;
if (od != null && od.OrganisationID != CurrentOrganisationID)
return null;
if (e == null)
return null;
return e;
}
The "All" method was a particular issue. Solved by How to conditionally filter IQueryable
private static readonly PropertyInfo _OrganisationIDProperty = ReflectionAPI.GetProperty<OrganisationDependent, int>(o => o.OrganisationID);
private static Expression<Func<TOrg, bool>> FilterByOrganization<TOrg>(int organizationId)
{
//The FilterByOrganisation method uses the LINQ Expressions API to generate an expression that will filter on organisation id
//This avoids having to cast the set using .AsEnumerable().Cast<OrganisationDependent>().Where(x => x.OrganisationID == CurrentOrganisationID).AsQueryable().Cast<T>();
//https://stackoverflow.com/questions/20052827/how-to-conditionally-filter-iqueryable-by-type-using-generic-repository-pattern
var item = Expression.Parameter(typeof(TOrg), "item");
var propertyValue = Expression.Property(item, _OrganisationIDProperty);
var body = Expression.Equal(propertyValue, Expression.Constant(organizationId));
return Expression.Lambda<Func<TOrg, bool>>(body, item);
}
public virtual IQueryable<T> All
{
get
{
if (typeof(T).IsSubclassOf(typeof(OrganisationDependent)))
return Context.Set<T>().Where(FilterByOrganization<T>(CurrentOrganisationID));
return Context.Set<T>();
}
}
This closes off most of the places that a user could access someone else's data. But it doesn't filter navigational properties. So I have to add code to all navigation properties on non-organisation dependent entities to do that.
I don't want to separate my data into different database, but one day I will find out if it's feasible to create views filtered by organisation in different schemas - with the same name and structure as my tables, then switch schema according to user.....oh and I want to automatically create them for each new organisation and autmatically migrate them using code-first too....
And you could vote to Allow filtering for Include extension method here
If you're using a CQRS style architecture you can think about having one or more viewmodels per user that contains the products/customers that they have access to.
If you see yourself having to implement logic on the query side of CQRS that is a strong indication that you are doing something wrong.

Linq2Sql custom mapping

My question is: is there a way with a DataContext/Table mapping to implement some custom mapping code between the database and the creation of my entity instance?
For a simple example, say that my Products entity class has property "double Length {get; set; }"
and my entity and all my existing code is expecting Length to be in Feet, but in the database Length is stored in meters so I need to do a conversion. Is there a way to register a callback or otherwise insert some code to set object.Length manually based on a DataRow or however the data is retrieved internally by L2S ?
Yes, I thought I could set "double LengthInMeters { set { Length=value*0.3048; } }" but I was hoping to avoid making duplicate properties and introducing ambiguity about which to use. What I'd like to do is register a closure with my DataContext to be called after a Product is created so that I can say:
DataContext.Entity<Product>().OnCreated( (p, row) =>
{
p.Length = row["Length"] * 0.3048;
p.ProductCode = row["Sku"].ToString().Substring(1, 5);
...etc..
});
You can do it in the post creation. the .designer.cs class contains the OnCreated() Extensibility Method for your models. You can put it there. I would not do this however. It hides important logic. Place this code in your repository or some similar abstraction. I personally would probably get the model, then transfer the data to an application specific model before returning it from the repository. Then I'd convert it back when returning it to the repository. Not very speedy, but business apps dont usually require millisecond speeds where I work... maintainability is paramount instead.
Rather than do a post-creation event, I would suggest you simply do the conversion when you instantiate your entity. For example:
var qry = from product in dc.Products
where product == something
select new Product()
{
Length = product.Length * 0.3048,
// etc.
};
var normalizedProduct = qry.ToList();

Categories

Resources