Related
I am developing an application in asp.net MVC, and for show multi-table in the same view, I have created one big ViewModel which contains the three models (employee, user, and request)
now my problem is my query that not work, and I can't locate the problem
Thank you for your help
Sorry for my english is not good
My bigviewmodel
namespace freegest.Models
{
public class ControleViewModel
{
public List<employe> employes { get; set; }
public List<demande > demandes { get; set; }
public List<utilisateur> utilisateurs { get; set; }
}
}
My query
public ActionResult listdemande()
{
int id = Convert.ToInt32(Session["id_utilisateur"]);
ControleViewModel CV = new ControleViewModel();
CV = (from a in CV.utilisateurs
join b in CV.demandes
on a.id_utilisateur equals b.idutilisateur_demande
join c in CV.employes
on a.idemploye_utilisateur equals c.id_employe
orderby c.nom_employe ascending
where a.id_utilisateur == id
select new ControleViewModel
{
c.nom_employe ,
});
return View(CV);
}
Try the following approach to join multiple tables using LINQ:
public ActionResult Index()
{
using (DBEntities db=new DBEntities())
{
List<Employee> employees = db.Employees.ToList();
List<Department> departments = db.Departments.ToList();
List<Incentive> incentives = db.Incentives.ToList();
var employeeRecord = from e in employees
join d in departments on e.Department_Id equals d.DepartmentId into table1
from d in table1.ToList()
join i in incentives on e.Incentive_Id equals i.IncentiveId into table2
from i in table2.ToList()
select new ViewModel
{
employee=e,
department=d,
incentive=i
};
return View(employeeRecord);
}
}
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;
I have developed many application now with Entity Framework Code First approach. In all of the I use Data Repository pattern. This Data Repository pattern queries over single entity at a Time. for e.g,
I have 2 models
Employee and Department
So to fetch all employees and department I would create 2 data repository instances. e.g
var empRepository = new DataRepository<Employee>();
var allEmployees = empRepository.GetAll();
var depRepository = new DataRepository<Department>();
var alldepartment = depRepository.GetAll();
Now, This pattern works great in most of the case. Now, When I want to perform join I can not do that in this pattern. I can perform join only after I have fetched all records of both entities and then i can use join on in-memory data. this creates extra overhead of 2 queries in my logic. Does any one have good pattern or solution that can be used with DataRepository pattern. Please suggest any alternatives to this pattern.
I actually just implemented a Join function in my generic repository just yesterday. It's a lot easier to do than how everyone's making it out to be, and you can use some cool features of Entity Framework while doing so.
To get started, I'm using a repository similar to what I wrote in this blog post. You'll notice that a number of the methods are returning IQueryable. This is no accident. IQueryable will allow to still use deferred execution, meaning that the query won't be run on the database until something forces it to (i.e. a loop or a .ToList() call). You'll see that, with putting the Join on this type of repository, that you won't end up needing to load up all the entities into memory to do the join, since all you'd be exposing is an IQueryable.
That being said, here's how I've got the Join in my repository, based off of the Queryable version of the Join method:
public IQueryable<TResult> Join<TInner, TKey, TResult>(IRepository<TInner> innerRepository, Expression<Func<T, TKey>> outerSelector, Expression<Func<TInner, TKey>> innerSelector, Expression<Func<T, TInner, TResult>> resultSelector) where TInner : class
{
return DbSet.Join(innerRepository.All(), outerSelector, innerSelector, resultSelector);
}
This, then, only makes one query to the database to get all of the information that you're requesting (again, since it only passes an IQueryable in the All() method).
with Repository pattern you can join two tables as in normal scenario.say you have defined your repository like this :
public interface IRepository<T>
{
T GetById(object id);
void Insert(T entity);
void Update(T entity);
void Delete(T entity);
IQueryable<T> Table { get; }
}
then you can join two table as following :
from p in _someRepository.Table
join p2 in _someOtherRepository.Table on p.FOO equals p2.BAR
with this approach there is no need to load all of table entries into memory.
Please take a look at the following links, in case you have not...
Social MSDN and
Forums.Asp.Net
There is a answer by #Link.fr which states as follows:-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace Repository
{
class Program
{
static void Main(string[] args)
{
// get all the informations about orders
CustomersRepository oCustomersRepository = new CustomersRepository();
ProductsRepository oProductsRepository = new ProductsRepository();
OrdersRepository oOrdersRepository = new OrdersRepository();
var query1 = oOrdersRepository.SelectAll().
Join(oCustomersRepository.SelectAll(),
order => order.CustomerId,
customer => customer.Id,
(order, customer) => new
{
MyOrder = order,
MyCustomer = customer
}).
Join(oProductsRepository.SelectAll(),
item => item.MyOrder.ProductId,
product => product.Id,
(item, product) => new
{
MyOrder = item.MyOrder,
MyCustomer = item.MyCustomer,
MyProduct = product }).
ToList();
foreach (var row in query1)
{
Console.WriteLine("query1 : {0} - {1}", row.MyCustomer.Name, row.MyProduct.Name);
}
Console.WriteLine("--");
Or
var query2 = (from order in oOrdersRepository.SelectAll()
join customer in oCustomersRepository.SelectAll() on order.CustomerId equals customer.Id
join product in oProductsRepository.SelectAll() on order.ProductId equals product.Id
select
new
{
CustomerName = customer.Name,
ProductName = product.Name
}).ToList();
foreach (var row in query2)
{
Console.WriteLine("query2 : {0} - {1}", row.CustomerName, row.ProductName);
}
Console.ReadKey();
}
}
The CLASS goes like:-
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Order
{
public int CustomerId { get; set; }
public int ProductId { get; set; }
}
public interface IRepository<T>
{
IList<T> SelectAll();
IList<T> SelectAll(Func<T, bool> expression);
}
public class CustomersRepository : IRepository<Customer>
{
public IList<Customer> SelectAll()
{
return new List<Customer>
{
new Customer{ Id = 1, Name = "Customer1"},
new Customer{ Id = 2, Name = "Customer2"},
new Customer{ Id = 3, Name = "Customer3"},
new Customer{ Id = 4, Name = "Customer4"}
};
}
public IList<Customer> SelectAll(Func<Customer, bool> expression)
{
return new List<Customer>
{
new Customer{ Id = 1, Name = "Customer1"},
new Customer{ Id = 2, Name = "Customer2"},
new Customer{ Id = 3, Name = "Customer3"},
new Customer{ Id = 4, Name = "Customer4"}
}.Where(expression).ToList();
}
}
public class ProductsRepository : IRepository<Product>
{
public IList<Product> SelectAll()
{
return new List<Product>
{
new Product{ Id = 1, Name = "Product1"},
new Product{ Id = 2, Name = "Product2"},
new Product{ Id = 3, Name = "Product3"},
new Product{ Id = 4, Name = "Product4"}
};
}
public IList<Product> SelectAll(Func<Product, bool> expression)
{
return new List<Product>
{
new Product{ Id = 1, Name = "Product1"},
new Product{ Id = 2, Name = "Product2"},
new Product{ Id = 3, Name = "Product3"},
new Product{ Id = 4, Name = "Product4"}
}.Where(expression).ToList();
}
}
public class OrdersRepository : IRepository<Order>
{
public IList<Order> SelectAll()
{
return new List<Order>
{
new Order{ CustomerId = 1, ProductId = 1},
new Order{ CustomerId = 1, ProductId = 2},
new Order{ CustomerId = 2, ProductId = 3},
new Order{ CustomerId = 3, ProductId = 4},
};
}
public IList<Order> SelectAll(Func<Order, bool> expression)
{
return new List<Order>
{
new Order{ CustomerId = 1, ProductId = 1},
new Order{ CustomerId = 1, ProductId = 2},
new Order{ CustomerId = 2, ProductId = 3},
new Order{ CustomerId = 3, ProductId = 4},
}.Where(expression).ToList();
}
}
}
May be it helps.
This is one of the reasons why repositories are a burden, especially generic repositories (one per entity type). If you try remain true to the pattern you always bump into some major problems.
You keep writing these clumsy join queries (linq). You can't really exploit the power of navigation properties. When queries get beyond simple this is a major pain.
So you're probably going to use navigation properties regardless. Now the question is: which repository is responsible for which entity? The goal of a query is to get children but a comfortable entry point is the parent.
You also have to have some unit of work (UoW) to collect and "transactionize" the CUD actions of separate repositories.
Why not use the context directly? See this post (it seems I keep quoting it). A context is a class that contains repositories (DbSets or ObjectSets) and keeps track of changes and transactions (UoW). Why wrap this unit of repositories and UoW in yet another layer of repositories and UoW's? That's two layers. And then, you're not going to expose your repositories to client code, because you don't want to expose IQueryables. So you're going to wrap repositories in a service layer that creates, orchestrates and disposes them. That's three layers.
(And repositories should expose IQueryable otherwise they're not composable).
But it is not testable, people say, we want to mock repositories so we can inject these mock repositories into our services for unit testing. That's an illusion. Mocking entity framework behaviour is deceptive and practically impossible. I'm not saying that unit tests are useless. I write unit tests for all kinds of business logic that should work once the objects exist. I just don't write unit tests to test data access code.
I have an entity like this:
public class Employment
{
public virtual Company Company {get; set;}
public virtual Person Person {get; set;}
public virtual string Description {get; set;}
}
providing a relationship between two other entities. They have corresponding DTO's and I want to return a result set containing all information on persons and companies. The query is performed on the Employment table and my problem is that Hibernate generates one select statement per company and person.
In my database, The Employment table has 1000 rows. Nhibernate generates 2001 select statements, one for the Employment list and one select for each Person and Company as I map them to DTO's.
I would like hibernate to fetch all information at once, in SQL I would have done something like this:
SELECT e.Description, c.A, c.B, c.C, p.D, p.E, p.F
FROM Employment e
JOIN Company c ON e.Company_Id = c.Company_Id
JOIN Person p ON e.Person_Id = p.Person_Id;
Or even
SELECT Description FROM Employment;
SELECT c.A, c.B, c.C FROM Employment e
JOIN Company c ON e.Company_Id = c.Company_Id;
SELECT p.D, p.E, p.F FROM Employment e
JOIN Person p ON e.Person_Id = p.Person_Id;
I am a pretty fresh user of nHibernate, QueryOver.I welcome Linq-To-Entities answers as well but I prefer to avoid LINQ query expressions.
I've looked all over the web, read about JoinQuery, JoinAlias and Fetch and have come as far as something like this:
//This works, but the objects are retrieved as PersonProxy and CompanyProxy,
//generating 2 SELECT statements for each Employment I map to EmploymentDto
var queryOver =
session.QueryOver<Employment>()
.Fetch(x => x.Person).Eager
.Fetch(x => x.Company).Eager
var mapResult = MappingEngine.Map<IList<EmploymentDto>>(queryOver.List());
//This works, but the objects are still retrieved as PersonProxy and CompanyProxy,
var queryOver =
session.QueryOver<Employment>()
.JoinAlias(x => x.Person, () => personAlias, JoinType.InnerJoin)
.JoinAlias(x => x.Company, () => companyAlias, JoinType.InnerJoin);
var mapResult = MappingEngine.Map<IList<EmploymentDto>>(queryOver.List());
JoinQuery provided the same result aswell. I feel I am missing something important here. Something should be done in the query or before .List() to fetch all child entities instead of loading a list with lots of Employment entities loaded with PersonProxy and CompanyProxy. I can, however, not find out how...
EDIT: Added mapping
Database tables:
TABLE Company(
Id,
A,
B,
C)
TABLE Person(
Id,
D,
E,
F);
TABLE Employment(
Person_Id,
Company_Id,
Description);
Entities
public class Company
{
public virtual string Id { get; set; }
public virtual string A { get; set; }
public virtual bool B { get; set; }
public virtual bool C { get; set; }
}
public class Person
{
public virtual string Id { get; set; }
public virtual string D { get; set; }
public virtual string E { get; set; }
public virtual string F { get; set; }
}
public class Employment
{
public virtual Person Person { get; set; }
public virtual Company Company { get; set; }
public virtual string Description { get; set; }
public override bool Equals(object obj)
{
Employment toCompare = obj as Employment;
if (toCompare == null)
return false;
return (this.GetHashCode() != toCompare.GetHashCode());
}
public override int GetHashCode()
{
unchecked
{
int results = Person != null ? Person.GetHashCode() : 0;
results = (results * 397) ^ (Company != null ? Company.GetHashCode() : 0);
results = (results * 397) ^ (Description != null ? Description.GetHashCode() : 0);
return results;
}
}
}
Mapping
public class CompanyMap : SyncableClassMap<Company>
{
public CompanyMap()
{
Table("Company");
Id(x => x.Id).Column("Id").GeneratedBy.Assigned();
Map(x => x.A).Column("A");
Map(x => x.B).Column("B").CustomType<YesNoType>();
Map(x => x.C).Column("C").CustomType<YesNoType>();
}
}
public class PersonMap : SyncableClassMap<Person>
{
public PersonMap()
{
Table("Person");
Id(x => x.Id).Column("Id").GeneratedBy.Assigned();
Map(x => x.D).Column("D");
Map(x => x.E).Column("E");
Map(x => x.F).Column("F");
}
}
public class EmploymentMap : ClassMap<Employment>
{
public EmploymentMap()
{
Table("Employment");
CompositeId()
.KeyReference(x => x.Person, "Person_Id")
.KeyReference(x => x.Company, "Company_Id");
Map(x => x.Description, "Description");
}
}
after your edit i see you have a keyreference instead of a normal many-to-one.
Unfortunatly this seems to be a limitation of QueryOver/Criteria which does not eager load keyreferences even with Fetchmode specified. However Linq to NH does not have this limitation. Change the query to
using NHibernate.Linq;
var results = session.Query<Employment>()
.Fetch(x => x.Person)
.Fetch(x => x.Company)
.ToList();
I have experienced the same problem you're describing here. I'll take your last code snippet as an example, since that's the way I've made it work:
//This works, but the objects are still retrieved as PersonProxy and CompanyProxy,
var queryOver =
session.QueryOver<Employment>()
.JoinAlias(x => x.Person, () => personAlias, JoinType.InnerJoin)
.JoinAlias(x => x.Company, () => companyAlias, JoinType.InnerJoin);
var mapResult = MappingEngine.Map<IList<EmploymentDto>>(queryOver.List());
First of all, you shouldn't have to specify JoinType.InnerJoin since that's the default join type. Same as with you, I've also found that the persons and companies are loaded lazily this way.
However, if you change the join type to JoinType.LeftOuterJoin, you'll see that everything is loaded eagerly. At least that's what I've experienced. So try change your code to the following:
//This works, but the objects are still retrieved as PersonProxy and CompanyProxy,
var queryOver =
session.QueryOver<Employment>()
.JoinAlias(x => x.Person, () => personAlias, JoinType.LeftOuterJoin)
.JoinAlias(x => x.Company, () => companyAlias, JoinType.LeftOuterJoin);
var mapResult = MappingEngine.Map<IList<EmploymentDto>>(queryOver.List());
I can't explain to you why it is this way, I've just found this to work out of own experiences. And if it's problematic for you doing a left outer join, you can instead try to filter accordingly in your code before (or while) doing the mapping.
This my method in my repository:
public List<Question> getallquestion()
{
var hej = from Core in db.CoreValue
join Question in db.Question on Core.CID equals question.QID
join Subject in db.SubjectType on Core.CID equals Subject.SID
select new
{
Core.Cname,
Question.QuestionText,
Subject.Sname
};
return hej.ToList();
}
This is the error:
Cannot implicitly convert type 'System.Collections.Generic.List<AnonymousType#1>' to 'NKI3.Models.Question'
What is the solution for this error? I cant seem to find it
My CreateViewModel:
public class CreateViewModel
{
public string QuestionText { get; set; }
public string Sname { get; set; }
public string Cname {get;set;}
}
}
Thanks in Advance!
Best Regards!
Your linq query produces List of anonymous object (as you use select new { (...) } without any type). You should replace it with select new Question() { (...) }. You should check that select syntax depending on Question class properties.
eg. When you have Name, Text and Subject property is should look like this:
var hej = from c in db.CoreValue
join q in db.Question on c.CID equals q.QID
join s in db.SubjectType on c.CID equals s.SID
select new Question
{
QID = q.QID,
QuestionText = q.QuestionText
};
Change your select to read like so:
public List<CreateViewModel> GetAllQuestionViewModel()
{
var hej = from Core in db.CoreValue
join Question in db.Question on Core.CID equals question.QID
join Subject in db.SubjectType on Core.CID equals Subject.SID
select new CreateViewModel
{
Cname = Core.Cname,
QuestionText = Question.QuestionText,
Sname = Subject.Sname
};
return hej.ToList();
}
In your controller you would do something like this:
public ActionResult Index() {
IEnumerable<CreateViewModel> questions = someContext.GetAllQuestionViewModel();
return View(questions);
}
Your method is expecting to return a collection of your Question type, but your LINQ query is returning a sequence of an anonymous type. You should change your query, for example changing your projection:
public List<Question> getallquestion()
{
var hej = from Core in db.CoreValue
join Question in db.Question on Core.CID equals question.QID
join Subject in db.SubjectType on Core.CID equals Subject.SID
select new Question
{
Name = Core.Cname,
Text = Question.QuestionText,
Subject = Subject.Sname
};
return hej.ToList();
}
This is using an object initializer to assign the property values of the Question type from the result of the query. Your property names will differ.
You can't return an anonymous type.
Create a class
public class QuestionResult
{
public string Core {get; set;}
public string Question {get; set;}
public string Subject{get; set;}
}
and use it
public List<QuestionResult> getallquestion()
{
var hej = from Core in db.CoreValue
join Question in db.Question on Core.CID equals question.QID
join Subject in db.SubjectType on Core.CID equals Subject.SID
select new QuestionResult
{
Core = Core.Cname,
Question = Question.QuestionText,
Subject = Subject.Sname
};
return hej.ToList();
}