I have a model, Docs, with a guid column, author_id, which should reference a MembershipUser.
I'm using the standard MembershipProvider (I only changed the db name), and of course I haven't a model for MembershipUsers.
I can easily define some methods in class Docs to get MembershipUser for a Doc and all Docs for a MembershipUser, but some things are tricky (listing users, show how many docs each user has); furthermore, I feel I'm using MVC in an odd way: it seems to me that it would be easier if I had a MembershipUsers model...
How can I implement this relationship? Should I implement a model for MembershipUsers?
thanks
update: I realize I wasn't clear at all.
Say I want to list all my users and their docs.
One way to do this is:
ViewModel model = new ViewModel()
{
Users = from MembershipUser user in Membership.GetAllUsers(page, pagesize, out totalusers)
select new UserWithDocs
{
User = user,
Docs = context.Docs.Where(c => c.author_id == (Guid) user.ProviderUserKey)
},
UsersCount = totalusers
};
This works, but generates one separate query for each user.
I could get an array of users' guids and then query for Docs where author_id IN list_of_guids, but then I should manually associate each doc to its author.
What is a better solution?
not sure if i understand you correctly, but you can use the MembershipUser class as your model for users. if you want to reference user to your "doc" model, you can simply add a property "Author" to you model class and when you fetch the model from DB you can get the user as well.
the Membership class contains some static methods to list all users, add/delete users etc.
Related
I am new to asp.net API and started figuring out things out of online tutorials and to Stack Overflow as well.
I am into a case where I need to build An API for Member_Product table where the transaction of memberships and other products purchase is registered and in order to extract members with a certain kind of product I have to have engage another table called Product_type.
What I know is you can use one model for a single controller to build API around it. I am still confused about that as I have to use more than on in my case.
Please what is the best practice about that and how to properly implement it and if there is anything I have to read and I miss it out, it's kind of you to provide link about it.
I have used an empty controller and used both models inside with a single LINQ query. Below the code int the member controller:
private readonly dboFFEntities FitnessDbo = new dboFFEntities();
[HttpGet]
[Route("Members/activeMembers/")]
public int GetLiveMembers()
{
using (FitnessDbo)
{
var LiveMemebersCount = (from mp in FitnessDbo.memberproductinfoes
join pt in FitnessDbo.product_type on mp.mepi_prodtype equals pt.prty_typeid.ToString()
where pt.prty_name == "MEMBERSHIP" &&
(mp.mepi_status == "1" || mp.mepi_status == "3" || mp.mepi_status == "6")
select mp.mepi_memberid).Distinct().Count();
return LiveMemebersCount;
}
}
The answer is DTO.
An API controller is not bound to a DB model or any other model. It represents one entity. There is no restriction/convention on how many tables a controller should access as long as the API design is good.
The Member_Product seems to be a Junction/Pivot table.
IMO you don't need a separate API controller for junction tables.
You can create an endpoint in the Members controller to return products for passed members.
For e.g.
api/products - Returns all the products in the DB (You may not want this)
api/products/{productId} - Returns single product with passed id.
api/members - Returns all the members (without products)
api/members/{memberId}/products - Return all the products for the passed member.
Ideally, you should not return DB models in response. Create DTOs that contains all the processed information required by the client.
For e.g.
Product DTO should only contain required info about the Product it should not contain information that is for internal use and will not be used by the client.
Member DTO should only contain member info.
The DTO for Member_Product (api/members/{memberId}/products) endpoint should contain all the info about products + member.
There are many articles on this problem refer
https://medium.com/#factoryhr/how-to-build-a-good-api-relationships-and-endpoints-8b07aa37097c
ViewModels in MVC / MVVM / Separation of layers- best practices?
Say we have something like the standard Book domain object and bookCategory object. In my controller I want to return a subset of list of books to the view. That subset is not achievable using a find query. When I try to filer the return object, it deletes relationships from the database!
I tried this:
class BookCategory{
String name
static hasMany = [books:Book]
}
class Book{
String title
}
def myController() {
def categories
categories = BookCategory.list()
def user = getCurrentUser()
categories.each { category ->
category.books.removeAll { book ->
!isBookBannedForThisUser(book.title, user)
}
[bookCategories: categories]
}
}
The problem is that it permanently removes these books from the categories for all users from the database!!!
I tried putting the method in a service and using a readonly transaction, but this did not help.
I assume that even if I copy all the categories and books into new list, they will still update the DB as they will still have the book IDs (which I need)
Saving to the database when you dont say save() is very dangerous. is there a way to disable this feature completely?
There is a fundamental flaw in your approach. Do not modify your domain instances if you don't intend to have the changes persisted. Doing so is going to cause you headaches.
Your domain model is suppose to be your system of record. Any changes to it are suppose to be persisted.
If you need to gather up data and manipulate it without having it reflected in your domain model then use a DTO (data transfer object) or similar pattern.
Simply calling .discard() will discard the changes you have made from being persisted when the session automatically flushes.
Instead of working against the framework, and disabling behavior, change your approach to be correct.
I´ve added some custom attributes to UserProfile asp.net membership table, lastname, address, cellphone, etc. So
I can create new users using.
WebSecurity.CreateUserAndAccount(UserName, Password,
propertyValues: new
{
UserId = model.userId,
UserLastName = model.lastName,
UserAddress = model.address,
.
.
}
);
So I want to know if it´s possible in similar way achieve an update query, including this custom attributes.
Thank you.
Yes you can. However I do not think WebSecurity provides a way to update extra column in membership tables e.g. UserProfile through its API.
This is how we did it, we have MVC4 ASP.NET internet application project and we are using EF5.0 Code First.
You already know how to add extra column in the UserProfile table (table name can be anything).
Once we have a class (that has all required extra columns along with UserId and UserName),
Added a controller UserController dedicated to facilitate CRUD operation for UserProfile.
UserController uses a UserService class in business layer that is taking care of all CRUD operation on UserProfile class (entity).
On Edit Post operation, controller call UserService class UpdateUser() method which looks like below:
public void UpdateUser(UserProfile user)
{
Guard.ArgumentNotNull(user, "user");
if (_roleWrapper.GetRolesForUser(user.UserName).Any())
{
_roleWrapper.RemoveUserFromRoles(user.UserName, _roleWrapper.GetRolesForUser(user.UserName));
}
if (!_roleWrapper.IsUserInRole(user.UserName, user.Role))
{
_roleWrapper.AddUserToRole(user.UserName, user.Role);
}
_dataContext.Update<UserProfile>(user);
}
Above text and sample is for example only, you can simplify it. Basically you need to get hold of UserProfile class and using DbContext to update it manually. WebSecurity has limited APIs keeping simplicity in mind.
Hope that helps (if anything confusing please let me know, i will expand further).
I'm using Entity Framework 5.0 for my MVC4 project. There's a problem with it. When i give a db model to any view, controller send model with no relationship
example;
I have User class and with relation departments
when i use it in controller
using(context)
{
var user = context.Find(id);
string department = user.Department.Name;
}
its working when call in context. but when i do that
using(context)
{
var user = context.Find(id);
return View(user);
}
and call in view like
Model.Department.Name
i got error.
Here is my answer but its not good
using(context)
{
var user = context.Find(id);
string department = user.Department.Name;
return View(user);
}
when i try to user Model.Department.Name in view i got no error i must do that for every relation when i use class as model. there is have better solution for this problem ? i want use all relationship in View without call these in controller.
I hope you can understand me, sorry my english.
On your DbContext you could use the .Include method to eagerly load the relations you need:
context.Users.Include(u => u.Department).FirstOrDefault(u => u.Id == id);
or if you are using an older version of entity Framework the generic version of this method might not be available:
context.Users.Include("Department").FirstOrDefault(u => u.Id == id);
The reason for this is that you haven't "loaded" the Department in your original code. As your context is wrapped in a using statement it's being disposed of before the view is created and therefore your user object lacks the data you want.
In your second code example you have specifically called into the related Department object and therefore it now exists within the User object.
You need to eager load the Department in your original line using something like
context.User.Include(c => c.Department).Find(id);
Now your user object should have this available in the view.
What are u trying to accomplish? List a view for a user with one or many departments?
At the table level I'm setting aspnet_User.UserID as foreign key of UserID in the other tables (like Posts.UserID, so each post has an owner). Is that the way to go?
When I create the LINQ to SQL model, should I include the aspnet_User table?
When I create a post (record in the Posts table, a Post object), how do I set the relationship to the logged in user (User object in the controller)?
I don't include the aspnet_User table to my linq-to-sql as I don't really need it. I use the built in way of accessing membership data.
But for your table Posts, it might be easier for you to include it so that you can easily display the User's Name by doing myPost.User.Name
edit:
MembershipUser user = Membership.GetUser();
Guid userGuid = (Guid)user.ProviderUserKey;
Post post = new Post
{
UserId =userGuid,
Message = message
};
In your database schema, you should definately have a the UserID in the Post table be a foreign key to the aspnet_user table, on the UserID field. This way, you are making sure your data is clean. I would even add a cascade delete & update on that relationship.
Then, refer to my instructions in a previous question you asked, about how to get the user data.
(Thomas Stock summed it up briefly, above, though :) )