I'm not very clear with writing linq queries. I write a query to select only certain columns from a table using linq lambda expression and I get the error that linq cannot be constructed to entities. The same query when I write using linq to select all columns I don't get any errors and I get all the columns, which i later filter out in the view. But I want to use the lambda to select only certain columns.
Code snippet:
ViewModel:
public class StaggingInternalCashExceptionViewModel
{
public OutputCash OutputCash { get; set; }
public IEnumerable<StaggingInternalException> StaggingInternalException { get; set; }
//list of results of Stagginginternalcashexception
}
Controller:
public ActionResult Exceptionstest(string dd1, string dd2, string dd3)
{
StaggingInternalExceptionViewModel _app = new StaggingInternalExceptionViewModel();
_app.StaggingInternalException = db2.StaggingInternalExceptions.Where(x => x.Level1 == dd1 && x.Level2 == dd2 ).Select(i => new StaggingInternalException
{
StaggingInternalRowID = i.StaggingInternalRowID,
Category = i.Category,
EnterText1 = i.EnterText1,
InternalAmount = i.InternalAmount,
ExternalAmount = i.ExternalAmount
});
_app.StaggingInternalException = (from p in db2.StaggingInternalExceptions
where p.LoadID==loadid && p.Level1 == dd1 && p.Level2 == dd2 select p);
}
In the above code, the lambda expression throws an error when I'm trying to select only certain columns from the table or if we are speaking in terms of entity classes, only certain properties. But the query returns all the columns. Should I be using DTOS? I'm not sure what the use of data transfer objects is. Some explanation on this would be great. Thanks.
You need to use a DTO.
A dto is just an object that you map your result to. In your case it would be
public class StaggingInternalExceptionViewModel
{
public int StaggingInternalRowID { get; set; }
public int Category { get; set; }
... //rest of properties
}
You need to change your StaggingInternalCashExceptionViewModel to use the StaggingInternalException DTO
public class StaggingInternalCashExceptionViewModel
{
public OutputCash OutputCash { get; set; }
public IEnumerable<StaggingInternalExceptionViewModel> StaggingInternalException { get; set; }
//list of results of Stagginginternalcashexception
}
Then your expression stays the basically the same but you select a new StaggingInternalExceptionViewModel instead of StaggingInternalException
StaggingInternalExceptionViewModel _app = new StaggingInternalCashExceptionViewModel();
_app.StaggingInternalException = db2.StaggingInternalExceptions.Where(x => x.Level1 == dd1 && x.Level2 == dd2 ).Select(i => new StaggingInternalExceptionViewModel
{
StaggingInternalRowID = i.StaggingInternalRowID,
Category = i.Category,
EnterText1 = i.EnterText1,
InternalAmount = i.InternalAmount,
ExternalAmount = i.ExternalAmount
});
Linq to Entities doesn't let you project a query using an entity type because you can end up losing information at loading an entity partially and trying later to save that entity to your DB. So, you must project your queries when you need partial information of an entity whether using a DTO or an anonymous type.
If you need to use the entity type, then don't project using Select method, the only thing is you're going to load all the properties, but I think this is not the case because you don't need all the data ;).
Related
I am trying this code I saw in a tutorial:
public ActionResult GetSearchRecord(string SearchText)
{
List<Unit_Of_Measurement> list = db.Unit_Of_Measurement.Where(x=>x.name.Contains(SearchText) && x.is_deleted == 0).Select(x => new Unit_Of_Measurement { name = x.name}).ToList();
return PartialView("SearchPartial", list );
}
This is the Unit_Of_Measurement model(I used db first approach when creating this):
public partial class Unit_Of_Measurement
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Unit_Of_Measurement()
{
this.Ingredients = new HashSet<Ingredient>();
}
public int id { get; set; }
public string name { get; set; }
public byte is_deleted { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Ingredient> Ingredients { get; set; }
}
But I am getting this error
The entity or complex type 'hwbModel.Unit_Of_Measurement' cannot be constructed in a LINQ to Entities query.
You cannot project into model type directly. Try using annonymous type or DTO, that is data transfer object.
Annonymous type
List<Unit_Of_Measurement> list = db.Unit_Of_Measurement.Where(x=>x.name.Contains(SearchText) && x.is_deleted == 0).Select(x => new { name = x.name}).ToList().Select(x => new Unit_Of_Measurement { name = x.name}).ToList();
Using DTO
Create new class for DTO.
public class UnitOfMeasurementDTO
{
public string Name { get; set; }
}
var list = db.Unit_Of_Measurement.Where(x=>x.name.Contains(SearchText) && x.is_deleted == 0).Select(x => new UnitOfMeasurementDTO { Name = x.name}).ToList();
Then you need to update your "PartialView" to support new DTO rather than Unit_Of_Measurement model.
Update
This is an Entity Framework (EF) restriction. LINQ to entities will convert your LINQ query in to command tree query which will execute against EF in order to return objects.
Construct an ObjectQuery instance from ObjectContext.
Compose a LINQ to Entities query in C# or Visual Basic by using the
ObjectQuery instance.
Convert LINQ standard query operators and expressions to command
trees.
Execute the query, in command tree representation, against the data
source. Any exceptions thrown on the data source during execution are
passed directly up to the client.
Return query results back to the client.
There are some restrictions when building command trees which can be execute against EF, so that project into model type directly does not support EF. Which means nothing wrong in your LINQ query, but cannot execute against EF. Anonymous types are not tracked in EF and completely separate from your model, so that it will work without any issues. These restrictions may have introduced later versions of EF, you might have used old sample code.
Hi and thank you for taking your time to read. I am having trouble calling from a db using entity framework for a particular row. Here is my code for controller.
public ActionResult MyAccount(CurrentAccount ca, SaverAccount sa, int id)
{
var model = db.CurrentAccounts.FirstOrDefault(_ => _.Id == id);
Session["Id"] = ca.Id;
Session["CurrentAccountNumber"] = ca.CurrentAccountNumber;
Session["CurrentBalance"] = ca.CurrentBalance;
Session["SaverAccountNumber"] = sa.SaverAccountNumber;
Session["CurrentBalance"] = sa.SaverAccountNumber;
return View(model);
}
My model is a edmx entity file and i can seem to retrieve some data to my locals but only from 1 table and i need data to be from multiple tables selecting a full row of data for a paricular Id then having this information visable on the same view. There is also a relation between id on both tables. Thanks :)
Here you have called wrong object because you are fetching data in model variable but calling from ca. please use as following
public ActionResult MyAccount(CurrentAccount ca, SaverAccount sa, int id)
{
var model = db.CurrentAccounts.FirstOrDefault(_ => _.Id == id);
Session["Id"] = model.Id;
Session["CurrentAccountNumber"] = model.CurrentAccountNumber;
Session["CurrentBalance"] = model.CurrentBalance;
Session["SaverAccountNumber"] = sa.SaverAccountNumber;
Session["CurrentBalance"] = sa.SaverAccountNumber;
return View(model);
}
You need to execute join query to get data from two models like following exmaple
Create a common class like follwing
public class datafrombothclass
{
public int Id { get; set; }
public String saveaccount_name { get; set; }
public String currrentaccount_name { get; set; }
}
Now use join query in entity framework to get data from both model in you case from CurrentAccount and SaverAccount.
See the bellow code example:
var frombothclass=(from a in Model.saveaccount join s in Model.currentaccountaccount
where a.Id=id
select new datafrombothclass{
Id=a.Id,
saveaccount_name=s.name,
currrentaccount_name=a.name
});
return View(frombothclass);
Hope you will get the solution.
I am trying to make a variable in my model that retrieves all available skills to later display them in a DDL. I have one variable that contains all of the skills and one variable that displays all of the skills an employee has already rated. I want to check all of the skills against the rated skills so that only the unrated skills are added to the new variable in the model. The only way I could think of to do this is to use a foreach loop to check for the values in the AllSkills variable.
model:
public IEnumerable<long> AvailableSkills { get; set; }
public IEnumerable<long> AllSkills { get; set; }
public IEnumerable <long> EmployeeRatings { get; set; }
public IEnumerable<Rating> Ratings { get; set; }
public IEnumerable<Skill> Skills { get; set; }
public long SkillId { get; set; }
Controller:
model.AllSkills = db.Skills.OrderBy(s => s.SkillName).Select(s => s.SkillId);
model.EmployeeRatings = db.Ratings.Where(r => r.EmployeeId == User.Identity.Name.Remove(0, 8)).OrderBy(s => s.SkillId).Distinct();
foreach (var skill in model.EmployeeRatings)
{
model.AvailableSkills = model.AllSkills.Where(s => s != skill);
}
I want to do something like the following code in the foreach loop:
model.AvailableSkills += model.AllSkills.Where(s=> s != skill); but this is not allowed.
The problem is that this for each loop above is assigning the model.AvailableSkills to the every skill but the last one that is in the foreach loop (as it should). How do I make it so that every one of the duplicate skills are excluded from model.AvailableSkills?
Using LINQ is IMO more readable if you use to query syntax opposed to the method chaining syntax.
model.AllSkills =
from skill in db.Skills
orderby skill.SkillName
select skill.SkillId;
model.EmployeeRatings =
from rating in db.Ratings
let employeeId = User.Identity.Name.Remove(0, 8)
where rating.EmployeeId == employeeId
orderby rating.SkillId
select rating.SkillId
You can use the Except() extension method to remove items from the collection.
// exclude the 'owned' skils
model.AvailableSkills = model.AllSkills.Except(model.EmployeeRatings);
And you probably want to distinct the results:
model.AvailableSkills = model.AvailableSkills.Distinct();
Last but not least:
Because you select the SkillId I'm unsure why you would order the results. This does not make sense especially because you order by different properties in both lists. Furtermore you probably want to select more details to display to the user, but to know this we need more details on your model.
Assuming EmployeeRatings contains rated skills, and you want AvailableSkills to have only skills not in EmployeeRatings but are in AllSkills, I think this is what you like to do:
model.AvailableSkills = model.AllSkills
.Where(s => !model.EmployeeRatings.Contains(s));
Consider two classes like this:
public class Skill
{
public int Id { get; set; }
}
public class Employee
{
public int[] RatedSkills { get; set; }
}
var skills = new List<Skill>
{
new Skill{ Id = 1}, new Skill{Id = 2}, new Skill{ Id = 3}, new Skill { Id = 4}
};
var emp = new Employee
{
RatedSkills = new int[] { 1,2 }
};
var availableSkills = skills.Select(s => s.Id).Except(emp.RatedSkills);
Console.Read();
The rating has an Id property an an employee has an int[] to hold his/her selected ratings. From there it's easy to filter
Following your current approach, you can use AddRange with a list. Something like:
List<long> availableSkills = new List<long>();
foreach (var skill in model.EmployeeRatings)
{
availableSkills.AddRange(model.AllSkills.Where(s => s != skill));
}
model.AvailableSkills = availableSkills;
Or you can achieve this with a more compact approach, and I believe Except removes dupes:
model.AvailableSkills = model.AllSkills.Except(model.EmployeeRatings);
I searched hours and hours for this without any luck. I'm trying to create a lambda expression to fetch data from two tables Schedule and Request. But i'm outputting a bool here. How can i do a proper left outer join to fix this?
this is the best i could come up with
ViewBag.RequestList = db.Requests
.Include(r => r.Department)
.Select(r => db.Schedules.Any(s => s.RequestId == r.RequestId));
but its a bool not a list.
Assume my table models are as follows
public class Request{
public virtual int RequestId { get; set; }
public virtual string Remarks { get; set; }
}
public class Schedule{
public virtual int ScheduleId{ get; set; }
public virtual string Name{ get; set; }
public virtual Request Request { get; set; }
}
I'm trying to see if each and every request has one or more schedules associated with it or not. so if i could attach schedule object to request and output it as a list then thats all i need.
But I want to do it using LINQ and lambda expressions and I've seen queries as below;
var leftList = (from emp in db.Requests
join d in db.Schedules
on emp.RequestId equals d.RequestId into output
from j in output.DefaultIfEmpty()
select new { RequestId = emp.RequestId,
name = emp.Department.Name,
route = emp.Route.Name });
But that's not what i want, because i have to specify every field i need in new { RequestId = emp.RequestId, name = emp.Department.Name, route = emp.Route.Name }
Thanks a lot!
just list what you want like this:
var leftList = from emp in db.Requests
join d in db.Schedules
on emp.RequestId equals d.RequestId into output
from j in output.DefaultIfEmpty()
select new
{
RequestId = emp.RequestId,
name = emp.Department.Name,
route = emp.Route.Name,
ScheduleId=j==null?0:j.ScheduleId,
SName=j==null?""j.Name,
};
I would like to use Linq and strongly typed views in the right way. at the moment I do the following:
Make a Model to verify agianst:
public class Menu
{
public int Id { get; private set; }
public string Text { get; private set; }
public string Action { get; private set; }
public string Controller { get; private set; }
public string Parameter { get; private set; }
public string Langue { get; private set; }
public Menu(int id, string controller, string action, string parameter, string text)
{
Id = id;
Controller = controller;
Action = action;
Text = text;
Parameter = parameter;
}
Use Linq to get the data from the database into the model:
public static List<Menu> GetTabListForMenu(string langue)
{
Page_dbEntities entity = new Page_dbEntities();
var tabList = (from ml in entity.wpmenulangue
where ml.Langue == langue
from m in entity.wpmenu
where ml.Menu == m.Id
from l in entity.wplangue
where ml.Langue == l.Langue
from p in entity.wppage
where p.Id == m.Page
select new { m.Id, p.Controller, p.Action, p.Parameter, ml.Text}).ToList();
List<Menu> menu = new List<Menu>();
foreach (var item in tabList)
{
menu.Add(new Menu(item.Id, item.Controller, item.Action, item.Parameter, item.Text));
}
return menu;
}
I am pretty convinced that this is not the optimal way to do this and have 2 questions:
When I get the data from the database I first use a var and then have to move it to the object with a foreach. this seems like a waste of both my time and less effeicent then getting it with sql.
I have been told that I can just verify up agianst the entitymodel. Even if i use multiple entities in a view. is this true? (the one telling me this wes not able to get it to work and I have not been able to find anything about it online).
I will try to look back on this post in the next couple of hours, but might have to wait 24 hours.
public static List<Menu> GetTabListForMenu(string langue)
{
Page_dbEntities entity = new Page_dbEntities();
return (from ml in entity.wpmenulangue
where ml.Langue == langue
from m in entity.wpmenu
where ml.Menu == m.Id
from l in entity.wplangue
where ml.Langue == l.Langue
from p in entity.wppage
where p.Id == m.Page
select new Menu(m.Id, p.Controller, p.Action, p.Parameter, ml.Text)
).ToList();
}
As for the validation is concerned you shouldn't use multiple entities in the view. You should use a single entity which is called ViewModel. This ViewModel is a class that represents the data on the view. If you are using DataAnnotations for validation you could decorate this view model properties with attributes that indicate how to be validated.