I have an application where I can choose a database with the help of a combobox and then apply several query operations on this database within application. For this purpose, I have generated 6 different .edmx files which is generated from my databases with the database first approach. Besides that, I want to create a Data Access Layer class where I am managing my data access operations, but I want this class to be generic so that I am not going to need to create 6 different data access classes for each entity. For this purpose, I created such a class;
public class UserDetails<TEntity> where TEntity:class
{
private DbContext _context { get; set; }
private DbSet<TEntity> efDB { get; set; }
public UserDetails(DbContext context)
{
_context = context;
efDB = _context.Set<TEntity>();
}
public List<UserDetailsModel> GetUserDetails(int? roleID, int? titleID, int? managerID, string id = "", string name = "", string surname = "", string mail = "")
{
using (efDB) {
List<UserDetailsModel> query = (from user in efDB.TABLE_USER
join pass in efDB.TABLE_PASSWORD_HISTORY on GetLatestPassword(user.ID) equals pass.ID into passJoined
from passLatest in passJoined.DefaultIfEmpty()
join role in efDB.TABLE_REL_USER_ROLE on user.ID equals role.UserID into roleJoined
from roleRel in roleJoined.DefaultIfEmpty()
join defRole in efDB.TABLE_DEF_ROLE on roleRel.RoleID equals defRole.ID into defRoleJoined
from defRoleRel in defRoleJoined.DefaultIfEmpty()
join title in efDB.TABLE_USER_TITLES on user.TitleID equals title.ID
where user.Name.Contains(name) && user.Surname.Contains(surname) && user.Email.Contains(mail)
select new UserDetailsModel() {
ID = user.ID,
Name = user.Name,
Surname = user.Surname,
Email = user.Email,
TitleID = (int)user.TitleID,
TitleName = title.Description,
Password = passLatest.Password,
RoleID = roleRel.RoleID,
RoleName = defRoleRel.RoleName,
}
).ToList();
return query;
}
As you see, I want to perform a linq query in my GetUserDetails method. Note that, all of the 6 databases have same tables with the same names so that this query is going to work for all of my entities. What I want to do here is that, use the variable efDBgenerically so that it can be any of the 6 database entites. The desired database entity instance will be created by a different class which works like a factory and will be given to my UserDetails data access class. But obvisiouly, my code won't work. How can I implement this generic logic into my system? Thanks in advance.
I think you want something like UnitOfWork. Here are a few links describing and explaining how to set one up.
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
https://www.codeproject.com/Articles/770156/Understanding-Repository-and-Unit-of-Work-Pattern
https://www.programmingwithwolfgang.com/repository-and-unit-of-work-pattern/
Related
a question about EntityFramework and SQLite.
When I work with SQL Server and I want to do a query over a table I would access the required table (for example "Messages") very easily like so:
_databaseDBContext.Messages.where(m => m.id == id);
But on SQLite I can not access the table by simple entering the name, instead I have use the Set<>() method like so:
_SqliteDBContext.Set<Message>().where(m => m.id == id);
It got me thinking what would happen if I had two tables like:
DbSet<Message> Messages { get; set; }
DbSet<Message> OtheTypes { get; set; }
How can I do to properly indicate each other?
Thanks!
The function .Set<T>() has an overload method that takes a table name; .Set<Message>(string name). So you can use that to differentiate between the two tables.
Where clause example:
var messages = _SqliteDBContext.Set<Message>("Messages").Where(x => x.id == 2);
var messages = _SqliteDBContext.Set<Message>("OtheTypes").Where(x => x.id == 2);
Example below using C# Query Language and the two database tables:
var query = from message in _SqliteDBContext.Set<Message>("Messages")
join otherType in _SqliteDBContext.Set<Message>("OtheTypes") on messages.id equals otherType.id
select new { message, otherType};
EF Core only supports having one mapping in the model for each entity type. I.e. there aren't any supported cases in which two instance of the same type could end up being stored in different tables or different columns.
But you can do a simple trick:
public class Messagetwo : Message
{
}
And then in context
DbSet<Message> Messages { get; set; }
DbSet<Messagetwo> OtheTypes { get; set; }
I've got an entity I created using Entity Framework's auto code gen and don't want to modify that entry, as I regenerate that code on every table alter. For the sake of a single EF query, I've created an inherited entity that adds one extra column, SessionId as showing below.
public class SpeakerWithSessionIdAdded : Speaker
{
public int SessionId { get; set; }
}
Where Speaker is defined as below but with about 100 total column (I do know there should be less columns, but it is was it is right now).,
public class Speaker {
public int Id {get;set;}
public string firstName {get;set;}
public string lastName {get;set;}
... MANY MORE COLUMNS
}
I have a LINQ query as follows where I want to get out all the columns plus the one I added (SessionId). Is there any easy way to do this without using either reflection, or by hand listing every column I want to show?
var speakersWithSessionIdAdded = await
(from sessionPresenter in _dbContext.SessionPresenter
join speaker in _dbContext.Speakerson sessionPresenter.SpeakerId equals attendee.Id
where sessionIds.Contains(sessionPresenter.SessionId)
select new SpeakerWithSessionIdAdded
{
SessionId = sessionPresenter.SessionId,
Id = attendee.Id,
UserFirstName = attendee.UserFirstName,
UserLastName = attendee.UserLastName,
Email = attendee.Email,
Username = attendee.Username,
UserBio = attendee.UserBio,
AllowHtml = attendee.AllowHtml
.. I DON'T WANT TO LIST OUT 100 MORE LINES HERE FOR EVERY PROPERTY
}).ToListAsync();
return speakersWithSessionIdAdded.ToLookup(r => r.SessionId);
}
I've having a lot of trouble doing what you would assume a simple task.
Changing user IDs to username when showing a table at my web application.
This is the data retrieved form the table:(unnecessary information removed)
var data = (from o in _mainDbContext.blog
select new blogViewModel
{
Id = o.id,
title = o.title,
Editor= o.editorID,
} );
editorID is the user ID created by ASP.Net Identity system. I need to load the corresponding user name and place it inside Editor fields for all the entries fetched.
What I have tried so far:
Something like:
var data = (from o in _mainDbContext.blog
join u in _userManager.Users on o.editorID equals u.UserName
select new blogViewModel
{
Id = o.id,
title = o.title,
Editor= o.editorID,
} );
Doesn't work because EF Core doesn't support joining tables from different contexts.
Using foreach like:
foreach (var row in data)
{
var user = await _userManager.FindByIdAsync(row.editorID);
row.Editor= user.UserName;
}
Doesn't work. It doesn't change the information inside data.
Trying to use raw SQL did not help either. Because FromSql works only on one table and ExecuteSqlCommand does not work with SELECT.
Currently, EF Core don't support query multiple DbContext with one query. You could trace this behavior Query: Throw if second context instance is used in single query #11101.
For a workaround, you may consider convert _userManager.Users to _userManager.Users.ToList() which is a list object.
var data = from o in _mainDbContext.Blogs
join u in _userManager.Users.ToList() on o.EditorID equals u.Id
select new BlogViewModel
{
Id = o.Id,
Title = o.Title,
Editor = u.UserName
};
var result = data2.ToList();
Why are your contexts separated ? Why not merge them and create a relation with a navigation property between Users and Blogs.
public class MainDbContext: IdentityDbContext<AppUser>
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
//Some properties
public int EditorId { get; set; }
public AppUser Editor { get; set; }
}
With this you can easily access user's info via the navigation property Editor.
I have a situation where I'd like to eager load the organization property of a user being retrieved from the DB using LINQ-to-SQL (because of my using here attempts to get the property later fail because the context's connection is closed). The problem is that sometimes the user does not have an organization. In that case FetchUserByName returns null. Not what I want at all. Is there a way to get this to work the way I want, which is to return an instance of user whether they have an associated organization or not?
private static void EagerLoad(DataContext ctx)
{
var loadOpt = new DataLoadOptions();
loadOpt.LoadWith<User>(u => u.Organization);
ctx.LoadOptions = loadOpt;
}
public User FetchUserByName(string username)
{
User user = null;
using (var ctx = contextManager.GetSingleDataContext())
{
EagerLoad(ctx);
user = ctx.GetTable<User>().SingleOrDefault(u => u.UserName == username && u.IsActive);
}
return user;
}
Documenting what the fix was for others who may run into this.
LoadWith actually determines whether to use an INNER JOIN or an OUTER LEFT JOIN in the SQL created by LINQ-to-SQL by looking whether the foreign key in the data model class is nullable in the database. If it is nullable it uses the outer join.
[Column]
public int OrganizationId { get; set; } /*** Here is The Problem ***/
private EntityRef<Organization> _organization;
[Association(OtherKey = "OrganizationId", Storage = "_organization", ThisKey = "OrganizationId", IsForeignKey = true)]
public Organization Organization
{
get
{
return this._organization.Entity;
}
}
Changed the one line to:
public? int OrganizationId { get; set; }
And then everything worked as expected.
I'm working with MVC3 and Entity Framework but i came to a point where i need more data from different tables. Usually i'd do something like this to get data from a table:
Table: Users
id
username
In code i would do something like this to get all the users:
public static IEnumerable<Users> GetUsers( int userId )
{
MyEntities ent = new MyEntities();
return from g in ent.Users
where g.OwnerUserId == userId
select g;
}
So this would give me all my users back.
But a user can join a group, and i have to get all the usernames from a specific group.
Table: userGroups
id
fk_user_id
fk_group_id
Now if i'd use this code:
public static IEnumerable<userGroups> GetUsersFromGroup( int groupId )
{
MyEntities ent = new MyEntities();
return from g in ent.userGroups
where g.OwnerUserId == userId
select g;
}
Now obviously this only returns me the data from the "userGroups" table. But somehow i also need the username from the Users table. How can i get that data too and still return my "userGroups" as an IEnumerable?
In SQL i'd simply do a LEFT JOIN, but i can't really figure out how that works here.
Something like this maybe:
var query = from g in ent.userGroups
join u in ent.Users on g.fk_user_id equals u.userID
select new { g, u, });
Or with a LEFT JOIN
var query = (from g in ent.userGroups
from u in ent.Users.Where(a => a.fk_user_id == u.userID).DefaultIfEmpty()
select new { g, u, });
var query = from ug in ent.userGroups
join u in ent.Users on ug.OwnerUserId = ug.userID
select new
{
Name = u.UserName,
Id = u.userID
Group = ug.GroupName
};
If you need left join then you would require DefaultIfEmpty.
Please check the following articles:
http://codingsense.wordpress.com/2009/03/08/left-join-right-join-using-linq
http://www.dotnetperls.com/join
The above queries are going to require you to change your method signature, which could be a lot of really painful work depending on where you have this set up. Arion's in particular pretty much totally mimics the left join behavior you're talking about (which is great, because you know what Entity is doing), but you will need to change your return type to a Tuple<userGroups, Users> or something of that nature.
What you may try instead is to update the userGroups poco to include a nav property to the Users table. If I'm understanding your posted question properly, you have a one-to-many relationship that exists here. In this case, you would change the poco as follows:
public class userGroups
{
public int ID { get; set; }
public string GroupName { get; set; }
public virtual ICollection<Users> Users { get; set; }
}
public class Users
{
public int ID { get; set; }
public string Name { get; set; }
public virtual userGroups UserGroup { get; set; }
}
However, the names you've posted in your original question are not what Entity considers normalized naming, so you may need to use data annotations as described here. Ctrl-F "ForeignKey" if you're having some trouble finding it, it's kind of a big infodump on data annotations as a whole.
The benefit is, if you link in like this you will never have to worry about joining again. You can simply access the Users collection on userGroups and it will be accessed, joined, and worked out all for you.