Query on using a class with LinqToSql - c#

I want to put the following code in a class by itself so that I can reuse it:
var activePersons = (from p in _dataContextOrders.Persons
select new
{
p.ID,
p.WindowsUserID,
p.Name,
p.CommonShortName,
p.TeamID,
p.Telephone,
p.Fax,
p.Email,
p.FMSBudgetOfficerID,
p.Active
}).Where(p => p.Active).OrderBy(p => p.CommonShortName);
So I can return the Object activePersons. I would replace all this with this:
var activePersons = DataAccessLayer.ActivePersons.GetActivePersons();
But further down the page, I have this:
var currentUser = activePersons.SingleOrDefault(p => p.WindowsUserID == strWindowsSessionUserId);
This now returns a compile error. Is there a way round this?

The reason you are getting the error is because of the anonymous object you are selecting in your query with new keyword. You can't return anonymous object from your method , so I guess you are returning object. Now for your method caller it is an object type object and it doesn't exposes all the properties selected in the query, (and you can't cast it to a type since you don't know the type) Hence the error.
You need to create a new class and with all the properties and return IEnumerable<yourClass> from the method.
There is a way to return anonymous object mentioned by Jon Skeet but he doesn't recommend it.
Define a class like:
class ReturnedObject
{
public int ID { get; set; }
public string WindowsUserID { get; set; }
//..... rest of the properties
}
and then in your query:
var activePersons = (from p in _dataContextOrders.Persons
select new ReturnedObject
{
ID = p.ID,
WindowsUserID = p.WindowsUserID,
//rest of the properties
in your method specify return type as:
public IEnumerable<ReturnedObject> GetActivePersons(//parameters

Related

Returning IQueryable type: The entity or complex type '' cannot be constructed in a LINQ to Entities query

I am running a query to populate options in a single select drop down menu. When I debug the function below, the query variable contains the resultset that I am expecting. However when I skip next to where it should be returned to, I get the error:
'The entity type or complex type 'Models.zz_Member' cannot be constructed in a LINQ to Entities query."
public IQueryable<zz_Member> GetMembers(string searchText)
{
var _db = new Portal.Models.AuthorizationContext();
IQueryable<zz_Member> query = _db.zz_Members;
return query //.Where(a => a.memLastName.Contains(searchText))
.Select(a => new zz_Member()
{
ID = a.ID,
memFirstName = a.memFirstName,
memLastName = a.memLastName
}
);
}
The zz_Member model object is defined as:
public class zz_Member
{
[ScaffoldColumn(false)]
public int ID { get; set; }
public string memFirstName { get; set; }
public string memLastName { get; set; }
}
The error is thrown when I try to convert to an IList, but when I check the value of memList using the debugger, it shows the error text in the results view.
IQueryable<zz_Member> memList = GetMembers(e.Text);
IList<zz_Member> memList2 = memList.ToList();
I have also tried writing the GetMembers functions to return the list as so:
public IList<zz_Member> GetMembers(string searchText)
{
var _db = new WWCPortal.Models.AuthorizationContext();
return (from m in _db.zz_Members
where m.memLastName.Contains(searchText)
select new zz_Member { ID = m.ID, memFirstName = m.memFirstName, memLastName = m.memLastName }).ToList();
}
Any hints or answers to why the resultset appears to not be getting returned to the caller and put into memList? Thanks.
You cannot use framework dependant/generated entities in projection (with select new), hence the error.
Looks like you are trying to select specific columns instead of all columns, your options are
Project to a new class with those specific members
return all fields/columns for your entities like:
Code:
return query.Where(a => a.memLastName.Contains(searchText)); //without `select new`

What should be the return type of this lambda expression?

Currently I am performing join on two tables, the result could be multiple rows.
The return type is IList<string> currently but I am getting an error
Error:
Cannot implicitly convert type System.Collections.Generic.List<AnonymousType#1> to System.Collections.Generic.IList<string>
Here is my expression
public IList<string> GetPendingSubGroups()
{
using (var db = new DataClasses1DataContext())
{
var pendingSubGroup =
db.sys_Log_Account_SubGroups.Where(subGroup => subGroup.cAuthorizedStatus.Equals("Pending")).Join(
db.sys_Account_Primary_Groups, subGroup => subGroup.nGroupCode, group => group.nGroupCode,
(subGroup, group) => new
{
cSubGroupName = subGroup.cSubGroupName,
cAddedBy = subGroup.cAddedBy,
dAddedOn = subGroup.dAddedOn
}).ToList();
return pendingSubGroup;
}
}
Can anyone help me out with this?
Create a class to represent your result
public class SubGroup
{
public string Name {get; set;}
public string AddedBy {get; set;}
public DateTime AddedOn {get; set;}
}
And return a list of that class
public IList<SubGroup> GetPendingSubGroups()
{
using(var db=new DataClasses1DataContext())
{
var pendingSubGroup = db.sys_Log_Account_SubGroups.Where(subGroup => subGroup.cAuthorizedStatus.Equals("Pending")).Join(db.sys_Account_Primary_Groups, subGroup => subGroup.nGroupCode, group => group.nGroupCode,(subGroup, group) => new SubGroup
{
Name = subGroup.cSubGroupName,
AddedBy = subGroup.cAddedBy,
AddedOn = subGroup.dAddedOn
}).ToList();
return pendingSubGroup;
}
}
note this change buried in the linq
(subGroup, group) => new *SubGroup*
{
Name = subGroup.cSubGroupName,
AddedBy = subGroup.cAddedBy,
AddedOn = subGroup.dAddedOn
}).ToList();
You are creating an Anonymous Type using the Select projection method here:
new
{
cSubGroupName = subGroup.cSubGroupName,
cAddedBy = subGroup.cAddedBy,
dAddedOn = subGroup.dAddedOn
}
The type is created by the compiler (as a class with read-only properties). The property names in this instance are cSubGroupName, cAddedBy, and dAddedOn.
If you need to return a string, perhaps you need to return one of these properties instead of the entire type?
You won't be able to implement a conversion between an anonymous type and string, so you'll likely need to either not create the type in the first place, just select the string property.
If you need to return a type and not just a string, then simply create a custom class with this properties and instead of making an anonymous type, make an instance of your class, as in NinjaNye's answer.
The error message pretty much says it all, you're returning a list of anonymous types:
(subGroup, group) => new
{
cSubGroupName = subGroup.cSubGroupName,
cAddedBy = subGroup.cAddedBy,
dAddedOn = subGroup.dAddedOn
}
If you just want the SubGroupName:
(subGroup, group) => subGroup.cSubGroupName
The return type is an anonymous type. You should create a new class with those properties and use it like so:
public IList<SomeClass> GetPendingSubGroups()
{
using(var db=new DataClasses1DataContext())
{
var pendingSubGroup = db.sys_Log_Account_SubGroups
.Where(subGroup => subGroup.cAuthorizedStatus.Equals("Pending"))
.Join(db.sys_Account_Primary_Groups, subGroup => subGroup.nGroupCode, group => group.nGroupCode,(subGroup, group) => new SomeClass(subGroup.cSubGroupName, subGroup.cAddedBy, subGroup.dAddedOn)).ToList();
return pendingSubGroup;
}
}
But it seems like you only want to return a string? In that case you can project just that one field:
return pendingSubGroup.Select(p => p.cSubGroupName).ToList();
You can return IList. But you will have to unpack it everytime you try to use it. I would not recomend to do so. It's better to define you own class on return it
As already answered, you create an anonymous type composed by the three properties :
cSubGroupName
cAddedBy
dAddedOn
If SubGroupName is what you want to get, then you can or amend your qu
public IList<string> GetPendingSubGroups()
{
using(var db=new DataClasses1DataContext())
{
var pendingSubGroup = db.sys_Log_Account_SubGroups.Where(subGroup => subGroup.cAuthorizedStatus.Equals("Pending")).Join(db.sys_Account_Primary_Groups, subGroup => subGroup.nGroupCode, group => group.nGroupCode,(subGroup, group) => subGroup.cSubGroupName).ToList();
return pendingSubGroup;
}
}

How do I return data from joined tables through subsonic's objects?

I'm using ActiveRecord on Subsonic 3 and I effectively want to do this:
select * from foo
left outer join bar on bar.Id = foo.barId
where foo.someProperty = 2
I've written a stored procedure to fetch the data but Subsonic has only created objects to hold the columns from foo and bar.
What's the best way of returning the data into a single object so I can just bind it. Ideally I want it to be in a list<> but without writing my own class, there doesn't seem to be a way provided by subsonic.
You have a couple options here...
You could create a database view that does your join, and have SubSonic generate a data type for your view, then your select would be just like selecting from any other table.
Alternatively, you could use a Linq expression to do the join into an anonymous or dynamic type (if you are using .net 4) For example:
public List<dynamic> LoadData(int id)
{
var data = from f in db.Foo
from b in db.Bar.Where(x => x.Id == f.BarId).DefaultIfEmpty()
where f.SomeProperty == id
select new
{
SomeProperty = f.Something,
AnotherProperty = b.SomethingElse
};
return data.Cast<dynamic>().ToList();
}
Of course another alternative is to do the Linq expression above, but define your own class to hold the returned data, and select into it.
public class MyData
{
public string SomeProperty { get; set; }
public string AnotherProperty { get; set; }
}
public List<MyData> LoadData(int id)
{
var data = from f in db.Foo
from b in db.Bar.Where(x => x.Id == f.BarId).DefaultIfEmpty()
where f.SomeProperty == id
select new MyData()
{
SomeProperty = f.Something,
AnotherProperty = b.SomethingElse
};
return data.ToList();
}

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};
}

Argument type System.Linq.IQueryable<> is not assignable to parameter type Systems.collection.generic.ienumerable

I am trying to concatenate two fields from a table and assign it as a list. Please see code below
public List<ForDropDownList> GetAccountsForDownList()
{
var accountList = new List<ForDropDownList>();
using (_context = new ApplicationContext())
{
accountList.AddRange( _context.Account
.Select(a => new
{
a.AccountId,
FullName = string.Concat(a.FirstName, a.LastName)
}));
}
return accountList;
}
This is my model
public class ForDropDownList
{
public int AccountId { get; set; }
public string FullName { get; set; }
}
I get this error:
Argument type System.Linq.IQueryable<{AccountId:int, FullName:string}> is not assignable to parameter type Systems.Collection.Generic.IEnumerable
Basically Your Select instanciates a new dynamic object which then you cannot add to your List<ForDropDownList>
The following code Should work:
accountList.AddRange(_context.Account.Select(a => new ForDropDownList { AccountId = a.AccountId, FullName = string.Concat(a.FirstName, a.LastName) }));
You'd better use the strong type instead of the "new {}" anonymous type. For example:
accountList.AddRange(_context.Account.Select(a => new ForDropDownList { AccountId = a.AccountId, FullName = string.Concat(a.FirstName, a.LastName) }));
C# cann't convert the anonymous type to the strong type.
FYI.
You should change your projection like below and call ToList at the end:
_context.Account
.Select(a => new ForDropDownList
{
a.AccountId,
FullName = string.Concat(a.FirstName, a.LastName)
}).ToList()
These changes are needed because:
Without specifying ForDropDownList the projection we do create instances of an anonymous type with the same properties as ForDropDownList. Since the type of the object we project is not ForDropDownList, we add the resulted collection to a collection of ForDropDownList objects (AddRange...)
The ToList() is needed to force the execution of the query and get the results in memory. The AddRange expects a collection. On the other hand you have just defined the query, you didn't request its execution. So as it was it couldn't be used as an argument for AddRange method.

Categories

Resources