In my model class OwnedModule i have OwnerID as Guid.
There is another model class called BusinessUnit which contains the same Guid value from OwnedModule and its OwnerName.
I want to show the details of OwnedModule, which contains all the details about the hardware but has the owner as Guid value and not the name, and for this i have created a view model
public class OwnedModuleViewModel
{
public long Id { get; set; }
public string ModuleId { get; set; }
public string TypeName { get; set; }
public string KindName { get; set; }
public string Owner { get; set; }
public DateTime OwnershipStart { get; set; }
}
public class IndexOwnedModule
{
public List<OwnedModuleViewModel> OwnedModules { get; set; }
}
to show all this detail in my repository i have following function
public IndexOwnedModule GetAllOwnedModules()
{
var modulesList = new IndexOwnedModule();
var modules = (_dbSis.OwnedModules.OrderBy(module => module.Id).Select(module => new OwnedModuleViewModel
{
Id = module.Id,
ModuleId = module.ModuleId,
TypeName = module.ModuleType.TypeName,
Owner = GetModuleOwner(module.ModuleOwnerId),//error here
OwnershipStart = module.Start
}));
modulesList.OwnedModules = modules.ToList();
return modulesList;
}
public string GetModuleOwner(Guid id)
{
var ownedModule =_dbSis.Set<BusinessUnit>().FirstOrDefault(t => t.Id == id);
if (ownedModule != null) return ownedModule.Name;
return null;
}
It would not be convinient to show the guid value as owner in the view to user so I wanted to fetch the name for which I had GetModuleOwnerName.
But it seems like the way i've set the name of the owner to my viewmodel view is wrong, and when i run the application i get the following error.
LINQ to Entities does not recognize the method 'System.String GetModuleOwner(System.Guid)' method, and this method cannot be translated into a store expression.
When i comment the line where I've set the value of owner(Owner = GetModuleOwner(module.ModuleOwnerId)), everything works fine.
When Entity Framework build your query, it relies on the inspection of the passed Expression Tree. When it encounter a method call, it will try to map it to an equivalent SQL method using (see this for the canonical methods). Because Entity Framework has no knowledge of OwnedModuleViewModel.GetModuleOwner, it cannot generate an appropriate SQL query. In this case, the simple way would be to embed what your method does in the query instead of calling a method:
_dbSis.OwnedModules
.OrderBy(module => module.Id)
.Select(module => new OwnedModuleViewModel
{
Id = module.Id,
ModuleId = module.ModuleId,
TypeName = module.ModuleType.TypeName,
Owner = _dbSis.Set<BusinessUnit>()
.Where(t => t.Id == module.ModuleOwnerId)
.Select(t => t.Name).FirstOrDefault(),
OwnershipStart = module.Start
});
Of course, that is assuming _dbSis.Set<BusinessUnit>() is a DbSet<T> part of the same DbContext, otherwise the same problem arise.
In Linq-To-Entities, a Linq statement against a context gets translated to a SQL statement. It's obviously impossible to translate the GetModuleOwner() method to SQL. You need to get the ModuleOwnerId first, and then in another step after, call GetModuleOwner() on each ModuleOwnerId.
Or you could restructure your query to use a join:
var modules = from m in _dbSis.OwnedModules
join b in _dbSis.BusinessUnit on m.ModuleOwnerId equals b.Id
order by m.Id
select new OwnedModuleViewModel {
Id = m.Id,
ModuleId = m.ModuleId,
TypeName = m.ModuleType.TypeName,
Owner = b.Name,
OwnershipStart = m.Start};
modulesList.OwnedModules = modules.ToList();
NOTE: I didn't test this so it might have some minor syntax errors.
This could be another option
var ownedModules = _dbSis.OwnedModules.OrderBy(module => module.Id).Select(module => new
{
Id = module.Id,
ModuleId = module.ModuleId,
TypeName = module.ModuleType.TypeName,
ModuleOwnerId = module.ModuleOwnerId,
OwnershipStart = module.Start
}).ToList().Select(m => new OwnedModuleViewModel
{
Id = m.Id,
ModuleId = m.ModuleId,
TypeName = m.TypeName,
Owner = GetModuleOwner(m.ModuleOwnerId),
OwnershipStart = m.OwnershipStart
});
ownedModulesList.OwnedModules = ownedModules.ToList();
return ownedModulesList;
Related
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 have a table where I store all the different code/value keywords that I need in my app:
public class Keyword
{
public int id { get; set;}
public string name { get; set; }
public string valuecode { get; set; }
public string valuename { get; set; }
}
Then I use Keyword to store records like these
name valuecode valuename
.DealState 1 Draft
.DealState 2 Final
.DealState 3 Cancelled
.DealType NEW New Business
.DealType RNW Renewal
.DealType WFA Waiting for approval
Then in other models I have fields that are filled using these keywords. For example,
public class Deal
{
....
public string state { get; set; }
public string type { get; set; }
....
}
I have managed to have the fields filled with "valuecode" while displaying "valuename" in Create and Edit views (I use DropDownList with a SelectList built in the controller), but I cannot find a way to display valuename instead of valuecode in Index and Details views.
I'm trying to pass the same SelectList in the ViewBag for Index, but then I do not know which syntax to use in order to replace the "state" code with the state "description" for each record returned.
Any hint?
PS: I'm quite new to .net and mvc, usually work with RoR and ActiveRecord...
EDIT
In my KeywordController I have a method
public SelectList selectKeywordValues(string kwname, object selectedKeyword = null)
{
var keywordsQuery = from d in db.Keywords
where d.name == kwname
orderby d.valuename
select d;
SelectList kwlist = new SelectList(keywordsQuery, "valuecode", "valuename", selectedKeyword);
return kwlist;
}
Then in my DealController i have the index method
public ActionResult Index()
{
var kw = new KeywordController();
ViewBag.state = kw.selectKeywordValues(".DealState");
return View(db.Deals.ToList());
}
SOLVED
In DealController the index method is the following
public ActionResult Index()
{
var kw = new KeywordController();
SelectList states = kw.selectKeywordValues(".DealState");
SelectList types = kw.selectKeywordValues(".DealType");
foreach (var item in db.Deals.ToList())
{
SelectListItem mystate = states.Where(row => row.Value == item.state).ElementAt(0);
SelectListItem mytype = types.Where(row => row.Value == item.type).ElementAt(0);
item.state = mystate.Text;
item.type = mytype.Text;
}
return View(db.Deals.ToList());
}
Now the db.Deals.ToList() is filled with descriptions and not with codes.
You can define a view model called DealViewModel that contains DealState and DealType properties. Then populate the DealViewModel with joins in LINQ before passing to the views that reference the view model.
Another approach is to use enums in EF5.
Let's say I have two models:
public class User
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
}
and
public class Friend
{
[Key]
public int FriendId { get; set; }
public User A { get; set; }
public User B { get; set; }
}
Let's say I only have 2 users in my database (ids: 1 (Jon) and 2 (Sam)). Now I insert into table friend like this:
db.Friends.Add(new Friend()
{
A = db.Users.Find(1),
B = db.Users.Where(u => u.UserId == 2).First()
});
db.SaveChanges();
Suddenly, I find a user (3, Sam) in a table user. What is the reasoning behind this? Not completely sure if relevant or not, but note that even if I make A and B fields virtual, nothing changes.
UPDATE
Finally found how to reproduce my problem. Apparently the problem isn't exactly the same as I described.
User a, b;
using (var db = new DbConnection())
{
a = db.Users.First(u => u.UserId == 1);
b = db.Users.First(u => u.UserId == 2);
}
using (var db = new DbConnection())
{
db.Friends.Add(new Friend()
{
A = a,
B = b
});
db.SaveChanges();
}
Now users will have 4 users. Does it mean that if I step out of transaction, I can no longer access the entities as if they were exactly the same items in the current transaction? Or maybe there is a way to make the program know that I am referring to the same item (because the ID is the same)?
Honestly tried the same steps as you described and everything work well.. Anyway my steps
Created a db context class derived from `DbContext'
public class EFContext : DbContext
{
public DbSet<Friend> Friends { get; set; }
public DbSet<User> Users { get; set; }
public EFContext(string connectionString)
: base(connectionString)
{
}
}
I use MSQL2008 Express with win auth so I created the Users table
using (var db = new EFContext(#"Data Source=yourMachineName\SQLEXPRESS2008;Initial Catalog=DBName;Integrated Security=True;MultipleActiveResultSets=True"))
{
db.Users.Add(new User()
{
UserId = 1,
Name = "John"
});
db.Users.Add(new User()
{
UserId = 2,
Name = "Sam"
});
db.SaveChanges();
}
I checked my db and found 2 records
After I created the Friends table
using(var db = new EFContext(#"Data Source=yourMachineName\SQLEXPRESS2008;Initial Catalog=DBName;Integrated Security=True;MultipleActiveResultSets=True"))
{
db.Friends.Add(new Friend()
{
A = db.Users.Find(1),
B = db.Users.Where(u => u.UserId == 2).First()
});
db.SaveChanges();
}
Again I got 1 record in the Friends table with columns FriendId=1, A_UserId=1, B_UserId=2.
I checked the Users table and I still have 2 records.
If I were you I would try my code in a separate app. If it works then please post here all steps which led you to this problem.
I have a model called Roles:
public string CodeRole { get; set; }
public string Organisation { get; set; }
public string LabelRole { get; set; }
CodeRole and LabelRole contain unique values, but the Organisation column contains about 12 categories. I want to generate a dropdown that allows the user to filter by Organisation.
As a result I want to construct a query using Entity Framework that returns some form of list/array/collection I can easily convert into a List<SelectListItem> with both text and value equal to the distinct Organisation values.
I assume the query would look something like this:
_context.Roles.GroupBy(r=> r.Organisation)
This returns an IGrouping<string,Roles> object, but I don't know how to use the IGrouping.
This would allow me to pass the List<SelectListItem> via a ViewBag to a dropdown list in the view.
Edit: Final Solution based of Alexander Manekovskiy response
List<Roles> orgs = (List<DimRoles>)_context.Roles.GroupBy(f => f.Organisation).Select(r => r.FirstOrDefault()).ToList();
List<SelectListItem> items = new List<SelectListItem>();
foreach (DimRoles r in orgs)
items.Add(new SelectListItem { Text = r.Organisation, Value = r.Organisation });
Yes, you are right about GroupBy, but then you will need to select only first values from groups:
_context.Roles.GroupBy(r=> r.Organisation).Select(r = r.First())
Another possible solution is to use Distinct extension method:
_context.Roles.Select(r=> r.Organisation).Distinct()
Then to get List<SelectListItem> you can use Select:
_context.Roles.GroupBy(r=> r.Organisation).Select(r =>
{
var organization = r.First();
return new SelectListItem() { Name = organization , Value = organization }
}).ToList();
But personally, I would prefer to have another extension method for converting IEnumerable<T> to List<SelectListItem>. This could be something like:
public static IEnumerable<SelectListItem> GetList<TEntity>(this IEnumerable<TEntity> collection, Expression<Func<TEntity, object>> keyExpression,
Expression<Func<TEntity, object>> valueExpression, object selectedValue = null)
{
var keyField = keyExpression.PropertyName();
var valueField = valueExpression.PropertyName();
return new SelectList(collection, keyField, valueField, selectedValue).ToList();
}
Then you can use it like this:
_context.Roles.Distinct(new OrganizationEqualityComparer()).GetList(o => o.Organization, o => o.Organization);
But in this case you will need to implement IEqualityComparer<Role> which is pretty simple:
class RoleOrganizationComparer : IEqualityComparer<Role>
{
public bool Equals(Role x, Role y)
{
if (Object.ReferenceEquals(x, y)) return true;
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.Organization == y.Organization;
}
public int GetHashCode(Role role)
{
//Check whether the object is null
if (Object.ReferenceEquals(role, null)) return 0;
//Get hash code for the Name field if it is not null.
return role.Organization == null ? 0 : role.Organization.GetHashCode();
}
}
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.