Join and override a model value using LINQ - c#

I want to override a string ID with a join when doing a query. At the moment I am using the database model as the view model, as Visual Studio automates. This is quite acceptable for my use, but I need to display the username not their ID. For a single candidate I have a achieved it thus for the Details view:
var ret = (from c in db.candidates
where c.candidate_id == id
join u in db.Users on c.createdby equals u.Id
select new { c, u.UserName }
).FirstOrDefault();
candidate theCandidate = ret.c;
theCandidate.createdby = ret.UserName; // Override with the username
Could I have done the theCandidate.createdby = ret.UserName within the LINQ query?
Or should I approach this differently?
(it seems to me that creating passing the anonymous type to the view is not ideal)
Edit
Or should I just change the database model to have the join implicitly:
class Candidate
{
...
[ForeignKey("createdby")]
public virtual ApplicationUser CreatedByUser { get; set; }
}

This is a typical case that proves the usefulness of ViewModels: your view needs something else compared to what your database delivers.
Also, your code puts something in createdby that does not belong there, assuming the datatypes allow it (what if createdby is an int?)
Perhaps it's time to get used to using this:
public class CandidateViewModel
{
// add properties that the View needs from database candidates table
string Username { get; set; } // the additional prop
public CandidateViewModel(candidate c, string username)
{
// ... obvious code here
}
}
And then
var ret = (from c in db.candidates
where c.candidate_id == id
join u in db.Users on c.createdby equals u.Id
select new CandidateViewModel(c, u.UserName)
).FirstOrDefault();
Edit:
With the new foreign key in place you could try something like this:
// using System.Data.Entity; <-- add this on top
var ret = (from c in db.candidates
where c.candidate_id == id
select c
)
.Include("CreatedByUser") // or "createdby" - not sure
.FirstOrDefault();
With that in place, try using ret.CreatedByUser.UserName in the controller, or even in the View. Note that my EF is a bit rusty, so you may have to try some things to get it right.

Related

Showing User Names (stored in ASP.Net Core Identity system), instead of IDs When loading a table

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.

left outer join in BAL using repository pattern - null exception

I don't quite understand why the following syntax, runs fine directly to Entity Framework.
var query = (from c in entitites.car
join cat in entitites.categories on c.code equals cat.code into categoriesGroup
from cg in categoriesGroup.DefaultIfEmpty()
select new CarListModel
{
Car = c,
CategoryDescription = cg == null ? null : cg.Description
});
var test = query.ToList(); //Success
While doing the same query on iqueryables from my repository fails with "The argument to DbIsNullExpression must refer to a primitive, enumeration or reference type." The repository uses the same entities object.
var queryCars = carRepository.GetIQueryable();
var queryCategory = categoryRepository.GetIQueryable();
var query = (from c in queryCars
join cat in queryCategory on c.Code equals cat.Code into categories
from cg in categories.DefaultIfEmpty()
select new CarListModel
{
Car = c,
CategoryDescription = cg == null ? null : cg.Description
});
var test = query.ToList(); // Fails!!!
Using the repository pattern, it does work if I change the syntax to
...
CategoryDescription = cg.Description
But if I mock up the repositories from List it failes with Object reference not set to an instance of an object.
I believe this has something do to with materialized / non-materialized data.
The .DefaultIfEmpty clearly returns a different result in these examples, and I was thinking of overriding with always returning NULL if empty. Not sure how I would go about this either.
The idea is to isolate the business logic to a service layer, and not in the repository. Though perhaps joining tables is "not" BL, and is fine in repository? How to do complex joins using this pattern?
Is is it not possible to do IQueryable joins using the repositories?
Update!
Added an example of the GetIQueryable method from the repository
public class Category
{
Public string Code {get;set;}
Public string Description {get; set;}
}
public IQueryable<Category> GetIQueryable()
{
return (from c in entities.categories
select new Category
{
Code = c.code,
Description = c.descripton
}).AsQueryable();
}
Problem is here:
public IQueryable<Category> GetIQueryable()
{
return (from c in entities.categories
select new Category
{
Code = c.code,
Description = c.descripton
}).AsQueryable();
}
You can't use new Category in query, because Category is your data model class (mapped to table). You have 2 options:
Rewrite query without new:
public IQueryable<Category>GetIQueryable()
{
return (from c in entities.categories
select c).AsQueryable();
}
Use another DTO objects as result:
class CategoryDto
{
public int Code {get;set;}
public string Description {get;set;}
}
//...
public IQueryable<CategoryDto> GetIQueryable()
{
return (from c in entities.categories
select new CategoryDto
{
Code = c.code,
Description = c.descripton
}).AsQueryable();
}

display data from 2 joined tables in MVC

I have two tables,and i am performing a left join on them,i want the resultant table to be displayed in my View, any ideas how to do that?
This is my controller section
public Actionresult display()
{
var joindata = (from c in dd.CategoryTbls join n in dd.SubCategoryTbls on c.Id equals n.CategoryId select new { ID=c.Id,CategoryName= c.CategoryName,SubCategoryName=n.SubCategoryName }).ToList();
return(joindata);
}
I cannot display it as a strongly typed view as the resultant table is join of two tables, so any ideas how to do it?
i want the resultant table to be displayed in my View
That's the wrong approach.
In MVC your views do not show 'a table' but they show 'a model'.
That change in wording is the solution: create a class for your joined records and create a strongly-typed View for that Model.
All you want is a View model..Make a class like ViewModelResult or anything like that
public class ViewModelResult
{
public YourTable1 YourTable1{get;set;}
public YourTable1 YourTable2 {get;set;}
}
and then make youir code as below :-
public Actionresult display()
{
ViewModelResult joindata = (from c in dd.CategoryTbls join n in dd.SubCategoryTbls on c.Id equals n.CategoryId select new ViewModelResult { ID=c.Id,CategoryName= c.CategoryName,SubCategoryName=n.SubCategoryName }).ToList();
return view(joindata);
}
and in your view bind ViewModelResult as model and there you can perform whatever you want to like showing data in grid via foreach and etc.
One way would be to use ViewBag
so instead of passing model to the view do this.
ViewBag.Joindata = (from c in dd.CategoryTbls join n in dd.SubCategoryTbls on c.Id equals n.CategoryId select new { ID=c.Id,CategoryName= c.CategoryName,SubCategoryName=n.SubCategoryName }).ToList();
You will then be able to access ViewBag from the view this is very dirty solution.
Example
#ViewBag.Joindata[0].ID
will contain first record.
Other option would be instead of using dynamic object to create model that does represent your viewModel and use strongly typed view, this would be more appropriate solution to your problem.
public class ThatSpecificViewModel
{
public ThatSpecificViewModel(int id, string categoryName, string subCategoryName)
{
this.ID = id;
this.CategoryName = categoryName;
this.SubCategoryName = subCategoryName;
}
public int ID { get; set; }
public string CategoryName { get; set; }
public string SubCategoryName { get; set; }
}
and select it this way
var joindata = (from c in dd.CategoryTbls join n in dd.SubCategoryTbls on c.Id equals n.CategoryId select new ThatSpecificViewModel(c.Id,c.CategoryName,n.SubCategoryName )).ToList();

Return data from 2 tables with Entity Framework

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.

The entity cannot be constructed in a LINQ to Entities query

There is an entity type called Product that is generated by entity framework.
I have written this query
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
The code below throws the following error :
"The entity or complex type Shop.Product cannot be constructed in a
LINQ to Entities query"
var products = productRepository.GetProducts(1).Tolist();
But when I use select p instead of select new Product { Name = p.Name}; it works correctly.
How can I preform a custom select section?
You cannot (and should not be able to) project onto a mapped entity. You can, however, project onto an anonymous type or onto a DTO:
public class ProductDTO
{
public string Name { get; set; }
// Other field you may need from the Product entity
}
And your method will return a List of DTO's.
public List<ProductDTO> GetProducts(int categoryID)
{
return (from p in db.Products
where p.CategoryID == categoryID
select new ProductDTO { Name = p.Name }).ToList();
}
You can project into anonymous type, and then from it to model type
public IEnumerable<Product> GetProducts(int categoryID)
{
return (from p in Context.Set<Product>()
where p.CategoryID == categoryID
select new { Name = p.Name }).ToList()
.Select(x => new Product { Name = x.Name });
}
Edit: I am going to be a bit more specific since this question got a lot of attention.
You cannot project into model type directly (EF restriction), so there is no way around this. The only way is to project into anonymous type (1st iteration), and then to model type (2nd iteration).
Please also be aware that when you partially load entities in this manner, they cannot be updated, so they should remain detached, as they are.
I never did completely understand why this is not possible, and the answers on this thread do not give strong reasons against it (mostly speaking about partially loaded data). It is correct that in partially loaded state entity cannot be updated, but then, this entity would be detached, so accidental attempts to save them would not be possible.
Consider method I used above: we still have a partially loaded model entity as a result. This entity is detached.
Consider this (wish-to-exist) possible code:
return (from p in Context.Set<Product>()
where p.CategoryID == categoryID
select new Product { Name = p.Name }).AsNoTracking().ToList();
This could also result in a list of detached entities, so we would not need to make two iterations. A compiler would be smart to see that AsNoTracking() has been used, which will result in detached entities, so it could allow us to do this. If, however, AsNoTracking() was omitted, it could throw the same exception as it is throwing now, to warn us that we need to be specific enough about the result we want.
There is another way that I found works, you have to build a class that derives from your Product class and use that. For instance:
public class PseudoProduct : Product { }
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products
where p.CategoryID== categoryID
select new PseudoProduct() { Name = p.Name};
}
Not sure if this is "allowed", but it works.
Here is one way to do this without declaring aditional class:
public List<Product> GetProducts(int categoryID)
{
var query = from p in db.Products
where p.CategoryID == categoryID
select new { Name = p.Name };
var products = query.ToList().Select(r => new Product
{
Name = r.Name;
}).ToList();
return products;
}
However, this is only to be used if you want to combine multiple entities in a single entity. The above functionality (simple product to product mapping) is done like this:
public List<Product> GetProducts(int categoryID)
{
var query = from p in db.Products
where p.CategoryID == categoryID
select p;
var products = query.ToList();
return products;
}
Another simple way :)
public IQueryable<Product> GetProducts(int categoryID)
{
var productList = db.Products
.Where(p => p.CategoryID == categoryID)
.Select(item =>
new Product
{
Name = item.Name
})
.ToList()
.AsQueryable(); // actually it's not useful after "ToList()" :D
return productList;
}
You can use this and it should be working --> You must use toList before making the new list using select:
db.Products
.where(x=>x.CategoryID == categoryID).ToList()
.select(x=>new Product { Name = p.Name}).ToList();
In response to the other question which was marked as duplicate (see here) I figured out a quick and easy solution based on the answer of Soren:
data.Tasks.AddRange(
data.Task.AsEnumerable().Select(t => new Task{
creator_id = t.ID,
start_date = t.Incident.DateOpened,
end_date = t.Incident.DateCLosed,
product_code = t.Incident.ProductCode
// so on...
})
);
data.SaveChanges();
Note:
This solution only works if you have a navigation property (foreign key) on the Task class (here called 'Incident').
If you don't have that, you can just use one of the other posted solutions with "AsQueryable()".
You can solve this by using Data Transfer Objects (DTO's).
These are a bit like viewmodels where you put in the properties you need and you can map them manually in your controller or by using third-party solutions like AutoMapper.
With DTO's you can :
Make data serialisable (Json)
Get rid of circular references
Reduce networktraffic by leaving properties you don't need (viewmodelwise)
Use objectflattening
I've been learning this in school this year and it's a very useful tool.
If you are using Entity framework, then try removing property from DbContext which uses your complex model as Entity
I had same problem when mapping multiple model into a viewmodel named Entity
public DbSet<Entity> Entities { get; set; }
Removing the entry from DbContext fixed my error.
if you are Executing Linq to Entity you can't use the ClassType with new in the select closure of query only anonymous types are allowed (new without type)
take look at this snippet of my project
//...
var dbQuery = context.Set<Letter>()
.Include(letter => letter.LetterStatus)
.Select(l => new {Title =l.Title,ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated,LetterStatus = new {ID = l.LetterStatusID.Value,NameInArabic = l.LetterStatus.NameInArabic,NameInEnglish = l.LetterStatus.NameInEnglish} })
^^ without type__________________________________________________________________________________________________________^^ without type
of you added the new keyword in Select closure even on the complex properties you will got this error
so remove the ClassTypes from new keyword on Linq to Entity queries ,,
because it will transformed to sql statement and executed on SqlServer
so when can I use new with types on select closure?
you can use it if you you are dealing with LINQ to Object (in memory collection)
//opecations in tempList , LINQ to Entities; so we can not use class types in select only anonymous types are allowed
var tempList = dbQuery.Skip(10).Take(10).ToList();// this is list of <anonymous type> so we have to convert it so list of <letter>
//opecations in list , LINQ to Object; so we can use class types in select
list = tempList.Select(l => new Letter{ Title = l.Title, ID = l.ID, LastModificationDate = l.LastModificationDate, DateCreated = l.DateCreated, LetterStatus = new LetterStatus{ ID = l.LetterStatus.ID, NameInArabic = l.LetterStatus.NameInArabic, NameInEnglish = l.LetterStatus.NameInEnglish } }).ToList();
^^^^^^ with type
after I executed ToList on query it became in memory collection so we can use new ClassTypes in select
In many cases, the transformation is not needed. Think for the reason you want the strongly type List, and evaluate if you just want the data, for example, in a web service or for displaying it. It does not matter the type.
You just need to know how to read it and check that is identical to the properties defined in the anonymous type that you defined. That is the optimun scenario, cause something you don't need all the fields of an entity, and that's the reason anonymous type exists.
A simple way is doing this:
IEnumerable<object> list = dataContext.Table.Select(e => new { MyRequiredField = e.MyRequiredField}).AsEnumerable();
It won't let you map back onto Product since that is your table you are querying. You need an anonymous function, then you can add it to a ViewModel, and add each ViewModel to a List<MyViewModel> and return these. It's a slight digression, but I include caveats about handling nullable dates because these are a pain in the behind to deal with, just in case you have any. This is how I handled it.
Hopefully you have a ProductViewModel:
public class ProductViewModel
{
[Key]
public string ID { get; set; }
public string Name { get; set; }
}
I have dependency injection/repository framework where I call a function to grab my data. Using your post as an example, in your Controller function call, it would look like this:
int categoryID = 1;
var prods = repository.GetProducts(categoryID);
In the repository class:
public IEnumerable<ProductViewModel> GetProducts(int categoryID)
{
List<ProductViewModel> lstPVM = new List<ProductViewModel>();
var anonymousObjResult = from p in db.Products
where p.CategoryID == categoryID
select new
{
CatID = p.CategoryID,
Name = p.Name
};
// NOTE: If you have any dates that are nullable and null, you'll need to
// take care of that: ClosedDate = (DateTime?)p.ClosedDate ?? DateTime.Now
// If you want a particular date, you have to define a DateTime variable,
// assign your value to it, then replace DateTime.Now with that variable. You
// cannot call a DateTime.Parse there, unfortunately.
// Using
// new Date("1","1","1800");
// works, though. (I add a particular date so I can edit it out later.)
// I do this foreach below so I can return a List<ProductViewModel>.
// You could do: return anonymousObjResult.ToList(); here
// but it's not as clean and is an anonymous type instead of defined
// by a ViewModel where you can control the individual field types
foreach (var a in anonymousObjResult)
{
ProductViewModel pvm = new ProductViewModel();
pvm.ID = a.CatID;
pvm.Name = a.Name;
lstPVM.Add(rvm);
}
// Obviously you will just have ONE item there, but I built it
// like this so you could bring back the whole table, if you wanted
// to remove your Where clause, above.
return lstPVM;
}
Back in the controller, you do:
List<ProductViewModel> lstProd = new List<ProductViewModel>();
if (prods != null)
{
// For setting the dates back to nulls, I'm looking for this value:
// DateTime stdDate = DateTime.Parse("01/01/1800");
foreach (var a in prods)
{
ProductViewModel o_prod = new ReportViewModel();
o_prod.ID = a.ID;
o_prod.Name = a.Name;
// o_prod.ClosedDate = a.ClosedDate == stdDate ? null : a.ClosedDate;
lstProd.Add(o_prod);
}
}
return View(lstProd); // use this in your View as: #model IEnumerable<ProductViewModel>
only add AsEnumerable() :
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products.AsEnumerable()
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}
you can add AsEnumerable to your collection like the follow :
public IQueryable<Product> GetProducts(int categoryID)
{
return from p in db.Products.AsEnumerable()
where p.CategoryID== categoryID
select new Product { Name = p.Name};
}

Categories

Resources