Entity Framework strategy/approach - entity-framework-6

Well, here goes another noob question: So I made this app where different customer have access to their accounts. Everything is ok, they login, by roles I can get who gets what though entity framework. The thing is, with time, the database will grow A LOT. For example: Customers have access to their "bills to pay". For now, there are only a couple thousand of them and a simple "Where" lambda expression can do the trick. But as said, the base will grow.
The scenario: Every record, has a "company" field, which determines the company which the record belongs to. Users have roles, where I store what companies that specific user can access data. So one user can access multiple companies data if it is configured this way.
My question is: Is there a way to initialize entity framework scope passing on the user's roles so the scope contains data which "belong" to that user? Something like:
using (MyThingy scope = new MyThingy(user.Roles))
{
//scope.Bills here will contain only bills which "payer" or "holder"
//are companies within user.Roles
List<Bill> billsToPay = scope.Bills.Where(c =>
c.DueDate == DateTime.Now);
}
So, is it possible? If so, what would be the best approach?

There are many ways to do this. You may want to read up on joins. Here are a couple of approaches that may work:
// brings back a lot of Bills from the db into memory......
using (DbContext scope = new DbContext())
{
//scope.Bills here will contain only bills which "payer" or "holder"
//are companies within user.Roles
IEnumerable<Bill> billsToPay = scope.Bills.Where(c => c.DueDate == DateTime.Now );
// this part happens in memory
List<Bill> bills = billsToPay
.Where(c => user.Roles.Any(role => c.payer == role.payer || c.holder == role.holder))
.ToList();
}
// more efficient.. I did this from memory. Syntax may not be perfect....
using (DbContext scope = new DbContext())
{
//scope.Bills here will contain only bills which "payer" or "holder"
//are companies within user.Roles
var query = from u in scope.Users
from role in u.Roles
from b in scope.bills.Where(b => b.DueDate == DateTime.Now && (b.roleID == role.roleID || b.holderID == role.holderID))
where u.userID == user.userID
select b;
}

Related

How to access 2 tables' relationship in a view in MVC

In my program I have two tables/models: Users and Groups in a many-to-many relationship.
When someone is logged in, I want to expose certain links that only Users in that specific Group can access.
Now in my view if I do:
#using (var dbContext = new DatabaseContext())
{
var user = dbContext.Users.First(p => p.Id == SecurityContext.CurrentUser.Id);
if (user.Groups.Count() < 1)
Debug.WriteLine("Less than 1");
}
And the list is always empty.
Is this something that view models are meant for? I need to somehow access both tables to find if a certain user is in a certain group.
You have to load related entities
var user = dbContext.Users.Include("Groups").First(p => p.Id == SecurityContext.CurrentUser.Id);
Read more about loading related entities on MSDN

Query all the entities where list property contains everything in another list

I have 3 entities, users, tasks and permissions. Users can have many permissions. Tasks also have many permission associated with them. The idea that I am trying to capture in my query is this: My tasks need to be done by users with all the correct permissions required by the task.
I am struggling with the query. I think this is what I need:
var userList = context.Users
.Include("Permissions")
.Where(x => neededPermissionFromTask.Except(x.Permissions).Count() == 0).ToList();
But I get an exception:
Unable to create a constant value of type 'Permission'. Only primitive
types ('such as Int32, String, and Guid') are supported in this
context.
Appreciate any help, suggestions or comments.
Instead of passing list of Permission objects, pass ids of those permissions:
var ids = neededPermissionFromTask.Select(p => p.Id).ToList();
var userList =
context.Users.Include("Permissions")
.Where(u => ids.Except(u.Permissions.Select(p => p.Id)).Count() == 0)
.ToList();
The exception is caused by your list neededPermissionFromTask - the Entity Framework has no idea how to deal with a list of Permission objects.
I would suggest something like this - find all users having all the permissions required by the task with ID taskId.
var users = context.Users
.Where(user => context.Tasks
.Single(task => task.Id == taskId)
.Permissions
.All(permission => user.Permissions
.Contains(permission)))
.ToList();
I am not sure if Permissions.Contains(permission) will work - it might cause the same error as mentioned in the question. But there is an easy way around - instead of looking if the object is contained in the set just look for an object with the same ID.

.NET web application - how to find logged in user's database records?

In my .NET CRUD web application I implemented MembershipProvider class.
Now I have function that lists records from database (this controller requires authenticated user). I need to filter out these records with respect to this logged-in user.
//
// GET: /Library/
public ViewResult Index(String orderBy = "")
{
var books = db.Books.Include(b => b.Category).Include(b => b.Writer).Include(b => b.User);
return View(books.ToList());
}
I need to know how to get logged in user's UserId and use it in Where condition (every tutorial I found talks about getting username but I need UserId). Thank you in advance.
Data I store in *.mdf data file.
I think that what you need is:
Membership.GetUser().ProviderUserKey
The type of this is object, but if you are using the out of the box membership, it should be a guid. This can then be used to filter your select accordingly.
Membership is part of the built in Membership Provider with a number of static methods.
Without knowing your DB structure/ORM, I can't exactly say, but it will be something like:
Guid userId = (Guid)Membership.GetUser().ProviderUserKey;
var books = db.Books.Where(b => b.UserId == userId);

EF Codefirst update many to many

I need some help on adding roles to user in a many-many situation.
So I have User with many Role and Role has many User.
I figured my current Update() method in my repository wont work. How can I build a repository method that allows me to remove all previous roles and add new roles to user?
This is what I currently have:
public User UpdateUser(User user, IEnumerable<Expression<Func<User, object>>> properties)
{
if (string.IsNullOrEmpty(user.UserId))
{
throw new InvalidOperationException("user does not exist");
}
else
{
db.Users.Attach(user);
foreach (var selector in properties)
{
string propertyName = Helpers.PropertyToString(selector.Body);
db.Entry(user).Property(propertyName).IsModified = true;
}
}
db.SaveChanges();
return user;
}
Is this the right way to update a user? I'm assuming everything the detached. This is how I'm calling this to add roles to user:
User user = new User();
user.UserId = userId;
user.Roles = new Domain.Role{ RoleId = 1}; //Assuming there is a role with ID = 1
userRepo.UpdateUser(user, new List<Expression<Func<User, object>>>
{
u => u.Roles
});
If you are working in a detached scenario you might want to look into the object state manager. See these two answers for more info.
Entity Framework Code First - Add Child Entity to Parent by Primary Key
Save a relation with between two entities an N-N association
The first one is a simple example where a single child is added without roundtriping the db. The second one is more complex but I still haven't found a good way to clear the relationship without telling it which childs to delete.
There is a possibility I haven't looked at yet and it is to use the RelationshipManager which you can get from the ObjectStateManager. It contains a few methods to get releated collections so maybe you could use that somehow.

Linq to SQL MVC issue with FK

right having a bit of a hard mental blog with some Linq to SQL and MVC.
Getting the basic MVC and L2SQL going ok.
FK relationships. -> when using MVC and we have a fk relationship in the model,
and the view is based on a strongly typed object - how do you get the data from the related table?
So for example
User (Table)
UserId
UserName
Call (Table)
CallId
UserId
CountryId
Country(Table)
CountryID
CountryName
SO I want to get only the calls for a user with a specific country?
The view - based on Call Object as this is the "Details" view -
how do i get a UserName and CountryName and still maintain a view based on Call?
It would seem that I still have to create an Object CallForUserByCountry - but this gets messy
on save as the CallForUserByCountry object also needs to implement how to create Call User and Country.
the linq query
var calls = from c in db.Call
where c.CountryID == id
select new CallForUserByCountry or new something// this is bit that suggests a new object.
Hopefully I missing something ...
If the Call data class has associations to User and Call data classes you can access Call's User and Country properties directly in your View. Also you can load data from related User and Country tables immediately (instead of lazy loading by default):
In Controller:
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Call>(x => x.User);
options.LoadWith<Call>(x => x.Country);
db.LoadOptions = options;
var calls = from c in db.Call
where c.Country.CountryName == countryName && c.User.UserName == userName
select c;
In View:
<%= Model.User.UserName %>
<%= Model.Country.CountryName %>
You could use the TempData bag and save the Username and CountryName there.
in the controller:
TempData["CountryName"] = countryName;
If I'm understanding this correctly you are trying to get calls from a specific user with a specific country. If that's the case, then as long as you have the relationships mapped in the Linq2SQL designer, then you should be able to access the user and country from the call as properties. So it would be something like:
var userName = call.User.UserName;
As I said, this is contingent on the relationships being mapped in the designer, otherwise Linq2SQL will have no indication that the two tables are related.
You could also have a separate query to just get the calls you need based on User and Country:
var calls = from c in db.Call
where c.User.UserID = userID
&& c.Country.CountryID == countryID
select c;
Your question can be taken two ways.
You want to display a list of calls using the current Call Details View. - This would need a new view. Unless the details view is a ViewUserControl. Then you could use PartialRender.
You want to add extra data to the current Details View - Add the extra data to the ViewData.
For number 2 you could do the following.
i prefer using joins for this, and I like working with objects detached from my DataContext. So normally i don't have the extra IQueryable in my objects.
I would still use the CallForUserByCountry object. Makes things verbose.
var calls = from call in db.Calls
join country in db.Countries on call.CountryId equals country.CountryID
join user in db.Users on call.UserId equals user.UserId
where user.UserName == userName && country.CountryName == countryName
select new CallForUserByCountry
{
UserName = user.UserName,
CountryName = country.CountryName,
Call = call
};
View["CallsForUserByCountry"] = calls.ToList();
In the view.
<% foreach(var callForUserByCountry in (IList<CallForUserByCountry>)ViewData["CallsForUserByCountry"]) { %>
.. Do stuff here .. I like using PartialRendering
<% Html.PartialRender("CallForUserByCountryDetail", callForUserByCountry); %>
<%}

Resources