Let's say have 3 tables:
Category
-------------
CategoryID int
Title text
Admin
------------
AdminID int
FullName text
Permission
------------
CategoryID int
AdminID int
AllowAccess bit
When i try to update changes to database i got following exception:
Unable to insert or update an entity because the principal end of the 'KiaNetModel.FK_Permissions_Admins' relationship is deleted.
WHY?
The function that update changes:
public static void SetPermissions(int[] cats, int userId, Entities context)
{
var premissions = from p in context.AdminPremissions where p.AdminID == userId select p;
// Clear all premissions...
foreach (var p in premissions)
{
p.AllowAccess = false;
}
foreach (var c in cats)
{
var es = from e in context.AdminPremissions where e.CategoryID == c && e.AdminID == userId select e;
// If any pre permission was found, set flag = true
if (es.Count() > 0)
es.First().AllowAccess = true;
// Otherwise add new one
else
context.AdminPremissions.AddObject(new AdminPremission() { AdminID = userId, CategoryID = c, AllowAccess = true });
}
}
It's an web application, and when user mark permissions, i could only determine which permissions are set, not all of them.
If you have any other idea, or better way please tell me.
I think relationship between tables Permission and Admin has been deleted from either Actual database or from Entity Model. It it is the case then u have to create that relationship again.
Generate "Admin" Table And "Permission" Drop And Create Script (With Data - If U Have Data On it).
Then Execute It, If U Have Relation Re Create It,
Ur Prb Must Be Solve.
Related
I have the following simplified setup:
Public User
{
//Primary key
public int Id {get;set;}
public string Name {get; set;}
}
Public UserInfo
{
//Primary key
public int Id {get;set;}
//Foreign key to user table
public int userKey {get; set;}
}
The relationship between the tables is one user to Many userInfo
I am trying to select from the user table and include the userInfo table.
I cannot do this:
var users = Context.user.Include(u => u.userInfos);
as there is no reference to the UserInfo table from the user table.
I can do this:
context.userInfo.include(x => x.user)
but if there are no corresponding entries in the userInfo table, this will not return any results, which is not what I want. Also, this will return one row for each userInfo, whereas I want one row for user, with a list of userInfo as a parameter.
Similarly I could join the tables like this:
var users = from us in Context.user
join inf in Context.userInfo
on us.Id equals inf.userKey
select new //etc
But this will also return one row per userInfo entry, the same problem as above.
To summarise, is there a way of including this table to produce a result in the same way as the include function does.
I know I could adjust my setup to all me to include this, but that is not what I am asking here.
I suspect this cannot be done, but from all my googling so far I have not been able to find a definitive answer....
I want one row for user, with a list of userInfo as a parameter
I assume you mean a list of userInfo as a property. My understanding of what you ask it that you're just looking for:
var users = from us in Context.user
join inf in Context.userInfo
on us.Id equals inf.userKey into infg
select new
{
User = us,
UserInfos = infg
};
join ... into amounts to a GroupJoin, i.e. a user entity joined with a group of userinfos.
Better still is to use a navigation property user.userInfos (reluctantly following your naming convention):
var users = Context.user.Include(u => u.userInfos);
Let Users be a database containing typical users data (name, email...) with an ID as primary key.
Let Applications be a database storing a list of applications (name, developer...) with an ID as primary key.
Let UsersApps be the mapping table between the two, using the primary key. UsersApps thus stores rows of ..., {102, user1_Id, appA_Id}, {103, userN_ID, appB_Id}, {104, user1_Id, appC_Id}, ...
And so on.
I want to retrieve a list of users data {name, email, List<Application> boughtApps}
I am struggling to find a LINQ request to do that and I am trying to do it in two steps, get a big table and then build each user's list of applications.
var q1 = from user in _dbContext.Users
join appUti in _dbContext.AppUsers on user.Id equals appUti.UsersId
join app in _dbContext.Applications on appUti.ApplicationId equals app.Id
orderby user.Id
select new UserDataWithApp { Id = user.Id, Name = user.Name, firstName= user.FirstName, Email = user.Email, App = app };
Then parse q1.toList() to build my required results list.
var qRes = q1.ToList();
int i = 0;
int j = 0;
while (i<qRes.Count())
{
listUsersWithApps[j] = qRes[i];
while (qRes[i].Id == listUsersWithApps[j].Id) // llist is ordered !!
{
listUsersWithApps[j].Apps.Add(qRes[i].Apps[0]);
i++;
}
j++;
}
Isn't there a better way ?
You can use navigation properties to allow the following:
var userApps = context.Users.Select(u => new UserWithApp(u.Name, u.Email, u.Applications))
Just add to following to User:
public virtual ICollection<Application> Applications { get; set; }
and to Application:
public virtual ICollection<Users> Users { get; set; }
So you can "navigate" between your entities and write to following query (just adapt your ordering and what user data to be seleted):
var userApps = from user in context.Users
select new UserDataWithApp { ..., BoughtApps = user.Applications }
See here for an example: http://www.entityframeworktutorial.net/code-first/configure-many-to-many-relationship-in-code-first.aspx
or another interesting blog: https://lostechies.com/jimmybogard/2014/03/12/avoid-many-to-many-mappings-in-orms/
I am working on application where I have a parent child relation between User and Orders tables. Below are the table structures:
User:
Column | DataType | Constraint
--------|-----------|-------------
UserId | int | Primary key
Username| varchar |
Email | varchar |
IsActive| bit |
Order:
Column | DataType | Constraint
--------|-----------|----------
OrderId | int | Primary key
UserId | int | Foreign key to User.UserId
Type | smallint |
Quantity| smallint |
As per the functionality I have to update User and his orders in one go where orders can be dynamically added, removed or updated.
I am using Entity Framework 6.0 and I cannot find any best solution to update parent table (user) and child table (order) in single transaction.
Could any one explain the best approach to achieve such functionality ?
Below is the code I am using:
Please take a note that I am using AutoMapper to map Object to Entity
public bool ManageUser(UserModel userDetails, string deletedOrders)
{
var isSuccess = false;
try
{
using (var entity = new UserEntity())
{
if (userDetails.UserId == 0)
{
entity.Users.Add(Mapper.Map<User>(userDetails));
}
else
{
var userToEdit = Mapper.Map<User>(userDetails);
foreach (var item in userToEdit.Orders)
{
if (item.OrderId == 0)
{
entity.Entry(item).State = System.Data.Entity.EntityState.Added;
}
else
{
entity.Entry(item).State = System.Data.Entity.EntityState.Modified;
}
}
/***
* How to Write a code here to remove orders using deletedOrders parameter
* Note: deletedOrders contains comma separated Id of the orders which needs to be removed. e.g. "1,5,6"
****/
entity.Entry(userToEdit).State = System.Data.Entity.EntityState.Modified;
}
entity.SaveChanges();
isSuccess = true;
}
}
catch (Exception ex)
{
}
return isSuccess;
}
Note: This is just a simple code. But what if I will have multiple child tables like Order ? If I would follow the same practice than code will become complex and hard to handle. So looking for best approach for such scenarios.
It is not necessary to make a trip to database for entities which should be deleted then.
If userDetails.Orders includes orders which should be deleted, you change their states inf your foreach (var item in userToEdit.Orders) cycle to System.Data.Entity.EntityState.Deleted.
If not, you simply create ones with suitable id's, attach them and then change their EntityState to Deleted as well:
var ordersToDelete = deletedOrders.Split(new[] {','},
StringSplitOptions.RemoveEmptyEntries).Select(x => new Order
{
Id = int.Parse(x.Trim())
})
.ToList();
ordersToDelete.ForEach(o =>
entity.Entry(x).State = System.Data.Entity.EntityState.Deleted);
I inherited an ASP.NET MVC 4 app that is using Entity Framework and keep getting duplicate records on related tables. I haven't been able to figure out why this is happening and it only happens randomly.
The models are as such:
Company -> has many Users -> has many UserRoles
UserRoles references the Roles table which has a RoleId and Name.
It is duplicating entries in the Roles table. I have been trying to figure this out but am pretty new with EF and some of that.
UserRole.cs partial class:
public static bool UpdateUserRoles(int userId, List<int> roleIds)
{
using (var context = new ImageTrackerEntities())
{
var userRoles = context.UserRoles.Where(r => r.UserId == userId).ToList();
foreach (var role in userRoles)
{
// do not remove users from super admin role
context.Entry(role).State = EntityState.Deleted;
}
foreach (var id in roleIds)
{
var ur = new UserRole() {
RoleId = id,
UserId = userId
};
context.Entry(ur).State = EntityState.Added;
}
context.SaveChanges();
return true;
}
}
To notice whats happened more precisely , try to activate EF Log so you can see the SQL generated (If you are using EF6 (current version of course)). Something like this :
using (var context = new BlogContext())
{
//YOU NEED TO ADD THIS
context.Database.Log = Console.Write;
var blog = context.Blogs.First(b => b.Title == "One Unicorn");
blog.Posts.First().Title = "Green Eggs and Ham";
blog.Posts.Add(new Post { Title = "I do not like them!" });
context.SaveChanges();
}
This will generate the following output:
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title]
FROM [dbo].[Blogs] AS [Extent1]
WHERE (N'One Unicorn' = [Extent1].[Title]) AND ([Extent1].[Title] IS NOT NULL)
-- Executing at 7/17/2015 10:55:41 AM -07:00
-- Completed in 4 ms with result: SqlDataReader
Some basics
I have two tables, one holding the users and one holding a log with logins.
The user table holds something like 15000+ users, the login table is growing and is reaching 150000+ posts.
The database is built upon SQL Server (not express).
To administer the users I got a gridview (ASPxGridView from Devexpress) that I populate from an ObjectDatasource.
Is there any general do’s and donts I should know about when summarizing the number of logins a user made.
Things are getting strangely slow.
Here is a picture showing the involved tables.
I’ve tried a few things.
DbDataContext db = new DbDataContext();
// Using foregin key relationship
foreach (var proUser in db.tblPROUsers)
{
var count = proUser.tblPROUserLogins.Count;
//...
}
Execution time: 01:29.316 (1 minute and 29 seconds)
// By storing a list in a local variable (I removed the FK relation)
var userLogins = db.tblPROUserLogins.ToList();
foreach (var proUser in db.tblPROUsers)
{
var count = userLogins.Where(x => x.UserId.Equals(proUser.UserId)).Count();
//...
}
Execution time: 01:18.410 (1 minute and 18 seconds)
// By storing a dictionary in a local variable (I removed the FK relation)
var userLogins = db.tblPROUserLogins.ToDictionary(x => x.UserLoginId, x => x.UserId);
foreach (var proUser in db.tblPROUsers)
{
var count = userLogins.Where(x => x.Value.Equals(proUser.UserId)).Count();
//...
}
Execution time: 01:15.821 (1 minute and 15 seconds)
The model giving the best performance is actually the dictionary. However I you know of any options I'd like to hear about it, also if there's something "bad" with this kind of coding when handling such large amounts of data.
Thanks
========================================================
UPDATED With a model according to BrokenGlass example
// By storing a dictionary in a local variable (I removed the FK relation)
foreach (var proUser in db.tblPROUsers)
{
var userId = proUser.UserId;
var count = db.tblPROUserLogins.Count(x => x.UserId.Equals(userId));
//...
}
Execution time: 02:01.135 (2 minutes and 1 second)
In addition to this I created a list storing a simple class
public class LoginCount
{
public int UserId { get; set; }
public int Count { get; set; }
}
And in the summarizing method
var loginCount = new List<LoginCount>();
// This foreach loop takes approx 30 secs
foreach (var login in db.tblPROUserLogins)
{
var userId = login.UserId;
// Check if available
var existing = loginCount.Where(x => x.UserId.Equals(userId)).FirstOrDefault();
if (existing != null)
existing.Count++;
else
loginCount.Add(new LoginCount{UserId = userId, Count = 1});
}
// Calling it
foreach (var proUser in tblProUser)
{
var user = proUser;
var userId = user.UserId;
// Count logins
var count = 0;
var loginCounter = loginCount.Where(x => x.UserId.Equals(userId)).FirstOrDefault();
if(loginCounter != null)
count = loginCounter.Count;
//...
}
Execution time: 00:36.841 (36 seconds)
Conclusion so far, summarizing with linq is slow, but Im getting there!
Perhaps it would be useful if you tried to construct an SQL query that does the same thing and executing it independently of your application (in SQL Server Management Studio). Something like:
SELECT UserId, COUNT(UserLoginId)
FROM tblPROUserLogin
GROUP BY UserId
(NOTE: This just selects UserId. If you want other fields from tblPROUser, you'll need a simple JOIN "on top" of this basic query.)
Ensure there is a composite index on {UserId, UserLoginId} and it is being used by the query plan. Having both fields in the index and in that order ensures your query can run without touching the tblPROUserLogin table:
Then benchmark and see if you can get a significantly better time than your LINQ code:
If yes, then you'll need to find a way to "coax" the LINQ to generate a similar query.
If no, then you are already as fast as you'll ever be.
--- EDIT ---
The follwing LINQ snippet is equivalent to the query above:
var db = new UserLoginDataContext();
db.Log = Console.Out;
var result =
from user_login in db.tblPROUserLogins
group user_login by user_login.UserId into g
select new { UserId = g.Key, Count = g.Count() };
foreach (var row in result) {
int user_id = row.UserId;
int count = row.Count;
// ...
}
Which prints the following text in the console:
SELECT COUNT(*) AS [Count], [t0].[UserId]
FROM [dbo].[tblPROUserLogin] AS [t0]
GROUP BY [t0].[UserId]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
--- EDIT 2 ---
To have the "whole" user and not just UserId, you can do this:
var db = new UserLoginDataContext();
db.Log = Console.Out;
var login_counts =
from user_login in db.tblPROUserLogins
group user_login by user_login.UserId into g
select new { UserId = g.Key, Count = g.Count() };
var result =
from user in db.tblPROUsers
join login_count in login_counts on user.UserId equals login_count.UserId
select new { User = user, Count = login_count.Count };
foreach (var row in result) {
tblPROUser user = row.User;
int count = row.Count;
// ...
}
And the console output shows the following query...
SELECT [t0].[UserId], [t0].[UserGuid], [t0].[CompanyId], [t0].[UserName], [t0].[UserPassword], [t2].[value] AS [Count]
FROM [dbo].[tblPROUser] AS [t0]
INNER JOIN (
SELECT COUNT(*) AS [value], [t1].[UserId]
FROM [dbo].[tblPROUserLogin] AS [t1]
GROUP BY [t1].[UserId]
) AS [t2] ON [t0].[UserId] = [t2].[UserId]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
...which should be very efficient provided your indexes are correct:
The second case should always be the fastest by far provided you drop the ToList() so counting can be done on the database side, not in memory:
var userId = proUser.UserId;
var count = db.tblPROUserLogins.Count(x => x.UserId == userId);
Also you have to put the user id into a "plain" primitive variable first since EF can't deal with mapping properties of an object.
Sorry, doing this blind since I'm not on my normal computer. Just a couple of questions
do you have an index on the user id in the logins table
have you tried a view specifically crafted for this page?
are you using paging to get the users, or trying to get all counts at once?
have you run sql profiler and watched the actual sql being sent?
Does something like this work for you?
var allOfIt = from c in db.tblProUsers
select new {
User = c,
Count = db.tblProUserLogins.Count(l => l.UserId == c.UserId)
}
.Skip(pageSize * pageNumber)
.Take(pageSize) // page size