I have scoured the net and was unable to find any example of conducting a join between two SharpRepository repos. Can anyone provide a link to a page or an example? I am attempting to conver the following linq expression into a sharp repo expression:
var user = (from f in _context.AccountUsers
join h in _context.Users on f.UserId equals h.UserId
where f.AccountId == accountId && h.UserName.Contains(email)
select new
{
h
});
return (IEnumerable<User>)user;
----- UPDATE ------
This is what I came up with, but it doesn't seem to be working propertly...
var aur = new AccountUserRepository();
var user = this.Join(aur, u => u.UserName.Contains(email), au => au.AccountId == accountId,
(u, au)=> u).AsQueryable().AsEnumerable();
return user;
There is a Join method on the repository that is similar to a LINQ Join statement and will let you join one IRepository<> with another IRepository<>. You pass it an IRepository<> to join with, an inner key selector, an outer key selector and a result selector.
You can look here for an integration test that uses it: https://github.com/SharpRepository/SharpRepository/blob/master/SharpRepository.Tests.Integration/RepositoryJoinTests.cs
The result of this call is another repository that you can then call GetAll, or FindAll, etc. on just like it was a normal IRepository<> itself. So I think you'll want to do something like this:
var accountUserRepo = new AccountUserRepository();
var userRepo = new UserRepository();
// join on the UserId column that is common to both, and select an anonymous type with both pieces of info (you would select just what you need)
var compositeRepo = accountUserRepo.Join(userRepo, au => au.UserId, u => u.UserId, (au, u) => new { AccountUserInfo = au, UserInfo = u } );
return compositeRepo.FindAll(x => UserInfo.UserName.Contains(email) && x.AccountInfo.AccountId == accountId, x => x.UserInfo);
That is how I think you would do it with the Join syntax.
If you have navigation properties like you would in EF you could probably just do this syntax which is simpler:
return accountUserRepo.FindAll(x => x.AccountId == accountId && x.User.UserName.Contains(email), x => x.User);
Related
I would like to create a method for common query. This query is joining three tables, and I would like to use property from all tables in the where clause.
The problem is when I create predicate on input then I can't implement it on selected anonymous object because all three tables are nested in anonymous object.
How should I implement this common method? I want to have it with different where conditions and use only one query.
The most common pattern for this is something like:
public async Task<IList<QueryResult>> GetQueryResults(
Expression<Func<Customer,bool>> customerFilter,
Expression<Func<Project, bool>> projectFilter )
{
var q = from c in Set<Customer>().Where(customerFilter)
join p in Set<Project>().Where(projectFilter)
on c.Id equals p.Id
select new QueryResult() { CustomerName = c.Name, ProjectName = p.Name };
return await q.ToListAsync();
}
Which you would call like:
var results = await db.GetQueryResults(c => c.Name == "a", p => true);
You can give the calling code more power by letting it change the base IQueryables used in the query, eg
public async Task<IList<QueryResult>> GetQueryResults(
Func<IQueryable<Customer>, IQueryable<Customer>> customerFilter,
Func<IQueryable<Project>, IQueryable<Project>> projectFilter )
{
var q = from c in customerFilter(Set<Customer>())
join p in projectFilter(Set<Project>())
on c.Id equals p.Id
select new QueryResult() { CustomerName = c.Name, ProjectName = p.Name };
return await q.ToListAsync();
}
which you would call like this:
var results = await db.GetQueryResults(c => c.Where(c => c.Name == "a"), p => p);
Running into a bit of trouble with my Linq Query. I'm attempting to join several tables and construct a new viewmodel to return to the details page, but running into a bit of a misunderstanding or incorrect syntax.
I have a details page for loads, but I want to include associated data from the trucks table which is joined through a linking table, load_trucks. Below is what I have right now, but is clearly incorrect.
var query = (from l in dbContext.Loads
where l.Id == id
join lt in dbContext.LoadTrucks on l.Id equals lt.LoadId into LoadTrucks
from lt in LoadTrucks.DefaultIfEmpty()
join t in dbContext.Trucks on lt.TruckId equals t.Id into Trucks
where Trucks.All(trk => LoadTrucks.All(ldtrk => ldtrk.TruckId == trk.Id))
from t in Trucks.DefaultIfEmpty()
join u in dbContext.Users on l.UserId equals u.Id into Users
where Users.All(usr => usr.Id == l.UserId)
from u in Users.DefaultIfEmpty()
join u2 in dbContext.Users on l.CreatedBy equals u2.Id into Users2
from u2 in Users2.DefaultIfEmpty()
join dr in dbContext.DriverReceipts on l.Id equals dr.LoadId into DriverReceipts
where DriverReceipts.All(drR => drR.LoadId == l.Id)
from dr in DriverReceipts.DefaultIfEmpty()
join le in dbContext.LogEntries on l.Id equals le.LoadId into LogEntries
where LogEntries.All(lgE => lgE.LoadId == l.Id)
from le in LogEntries.DefaultIfEmpty()
select new LoadsViewModel()
{
Id = l.Id,
LoadId = l.LoadTag ?? l.Id.ToString(),
LoadCreatedBy = u2.FullName,
TruckList = Trucks.ToList(),
UserList = Users.ToList(),
DriverLogEntries = LogEntries.ToList(),
ReceiptList = DriverReceipts.ToList()
});
Any help on this would be great. Thanks!
You need to add :
.FirstOrDefault();
at the end of your query.
That will effectively materialize your query to a single instance of:
LoadsViewModel
You could also wait until the last moment to call .FirstOrDefault() just before where you actually need it.
I see some related questions on stack, but not sure how to still solve it for my problem as I'm doing a three table left outer join. RoleUser and Role are an entity object
var query = from u in Context.Users
join ru in Context.RoleUsers on u.Id equals ru.UserId into plv
from x in plv.DefaultIfEmpty(new RoleUser())
join r in Context.Roles on x.RoleId equals r.Id into pii
from y in pii.DefaultIfEmpty(new Role())
orderby u.Id, y.Id
select new UserWithRole() { Id = u.Id, User = u, Role = y };
The problem is DefaultIfEmpty(new RoleUser()) and DefaultIfEmpty(new Role()) calls, because EF cannot translate creation of a constant entity object to SQL.
So you need to use the parameterless overload of DefaultIfEmpty() and deal with null objects in the materialized query result.
If you don't need a IQueryable<T> result, you can use intermediate anonymous type projection, which to LINQ to Objects context by using AsEnumerable() and do the final desired projection:
var query = (from u in Context.Users
join ru in Context.RoleUsers on u.Id equals ru.UserId into userRoles
from ru in userRoles.DefaultIfEmpty()
join r in Context.Roles on ru.RoleId equals r.Id into roles
from r in roles.DefaultIfEmpty()
orderby u.Id, r.Id
select new { User = u, Role = r }
)
.AsEnumerable() // db query ends here
.Select(x => new UserWithRole
{
Id = x.User.Id,
User = x.User,
Role = x.Role ?? new Role() // now this is supported
});
As the error explains you cannot create in a linq query they mapped types (RoleUser and Role). Instead use the DefaultIfEmpty(). Something along the following:
var roles = from ru in Context.RoleUsers
join r in Context.Roles on ru.RoleId equals r.Id
select new { ru.UserId, role = r };
var query = from u in Context.Users
join r in roles on u.Id equals r.UserId into plv
from x in plv.DefaultIfEmpty()
select new UserWithRole() { Id = u.Id, User = u, Role = x.role };
However I'd suggest look into navigation properties - that way you will not have to construct the joins and work with it as if it is "normal" objects.
How can I achieve a join and a where in C# MVC using something like Linq or EF Join?
This is the equivalent SQL I am trying to achieve.
select * from promotion P
JOIN PromotionsClaimed PC
on PC.PromotionId = P.objectid
where PC.userId = #USERID
This method should return a list of the promotions for the user. First, I get a list of all of the promotions, then I get the composite list of claimed promotions for the user. This is what I have so far.
public IList<Promotion> GetRewardsForUser(string userId)
{
//a list of all available promotions
IList<Promotion> promos = _promotionLogic.Retrieve();
//contains a list of Promotion.objectIds for that user
IList<PromotionsClaimed> promosClaimed = _promotionsClaimedLogic.RetrieveByCriteria(t => t.userId == userId);
//should return a list of the Promotion name and code for the rewards claimed by user, but a complete list of Promotion entities would be fine
var selectedPromos =
from promo in promos
join promoClaimed in promosClaimed on promo.objectId equals promoClaimed.PromotionId
select new { PromoName = promo.Name, PromoCode = promo.Code };
return selectedPromos;
}
I realize there are a lot of problems here. I'm trying to learn Linq and Entity Framework, but I don't know how to add the where clause to an IList or if there is an easier way to accomplish this.
It seems to me like there would be a way to filter the promotion list where it contains the Promotion.objectId in the promosClaimed list, but I don't know the syntax.
public IList<Promotion> GetRewardsForUser(string userId)
{
//a list of all available promotions
IList<Promotion> promos = _promotionLogic.Retrieve();
//contains a list of Promotion.objectIds for that user
IList<PromotionsClaimed> promosClaimed = _promotionsClaimedLogic.RetrieveByCriteria(t => t.userId == userId);
//should return a list of the Promotion name and code for the rewards claimed by user, but a complete list of Promotion entities would be fine
var selectedPromos =
(from promo in promos
join promoClaimed in promosClaimed on promo.objectId equals promoClaimed.PromotionId
select new { PromoName = promo.Name, PromoCode = promo.Code }).ToList();
return selectedPromos;
}
If I understood your question correctly, you could do something like this:
public IList<Promotion> GetRewardsForUser(string userId)
{
//contains a list of Promotion.objectIds for that user
IList<PromotionsClaimed> promosClaimed = _promotionsClaimedLogic
.RetrieveByCriteria(t => t.userId == userId);
var promotionIds = promosClaimed.Select(p => p.PromotionId).ToList();
IList<Promotion> promos = _promotionLogic.Retrieve()
.Where(p => promotionIds.Contains(p.objectId))
.Select(p => new { PromoName = p.Name, PromoCode = p.Code });
return selectedPromos;
}
The claimed promotions should be already filtered by a user so this should possibly work.
First of all, you're using entity framework? Or are you just trying to do join of two collections?
Because if you are using EF, you're thinking the wrong way. In the entity the proper way is to use include, for example:
public DbSet<promotion > promotion { get; set; }
public DbSet<PromotionsClaimed> PromotionsClaimed{ get; set; }
Context.promotion.Include(o => o.PromotionsClaimed).FirstOrDefault(s => s.Id == USERID);
If you need only join two collection using linq, you can do that.
var userId = 1;
var test =
(
from p in promos
join pc in promosClaimed on p.objectid equals pc.PromotionId
where pc.userId == userId
select p
).ToList();
Have you tried just to add your condition to your code? Like:
var selectedPromos =
from promo in promos
join promoClaimed in promosClaimed on promo.objectId equals promoClaimed.PromotionId
where promosClaimed.UserId == userId
select new { PromoName = promo.Name, PromoCode = promo.Code };
That should work or I just didn't understand you
I am trying to pull the results from the database and set the child property while selecting using the Linq (EF V5.0). The reason I am doing this is because there is no relation in the database to use include..
var lamdaResult = from u in model.Entity_Users
join s in model.Entity_Staff on u.UserID equals s.ST_UserID
select new { u, s };
return lamdaResult.Select(x => x.u.Staff = x.s; return x.u;).FirstOrDefault();
I am learning Linq.. the above expression is giving me error.. can someone help me the best way to set the child property...
I could also do this.. but I am wondering is there any better way to fulfill the same result instead of following 2 expressions
var user=null;
var lamdaResult = from u in model.Entity_Users
join s in model.Entity_Staff on u.UserID equals s.ST_UserID
select new { u, s };
user = lamdaResult.Select(x => x.u).FirstOrDefault();
user.Staff = lamdaResult.Select(x => x.s).FirstOrDefault();
Linq is for querying, not for mutating objects. You'll need to use something other than LINQ to do the mutation, generally a foreach is appropriate, although given that you only have a single item, there's no need for even that:
var item = (from u in model.Entity_Users
join s in model.Entity_Staff on u.UserID equals s.ST_UserID
select new { User = u, Staff = s })
.FirstOrDefault();
item.User.Staff = item.Staff;
return item.User;