I want to return something like this from the DBDataController to be consumed by upshot.
public IQueryable<ExternalUser> GetUsers()
{
return DbContext.Users.OrderBy(u => u.UserId).Select(x => new ExternalUser(x));
}
Where Users is of type User:
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
And where ExternalUser is:
public class ExternalUser
{
Models.User user;
public ExternalUser(Models.User user)
{
this.user = user;
}
public int UserId { get { return user.UserId; } set { user.UserId = value; } }
public string UserName { get { return user.UserName; } set { user.UserName = value; } }
}
The objective is to put a lot of business logic in those intermediate Entities. For security, performance and elegance reasons there are always things in the database that you want exposed to your views in a slightly or sometime radically different structure.
But when I try to run that code I get an error message: "queryOperation '{0}' must return an entity type or an IEnumerable/IQueryable of an entity type"
So I'd like to be able to easily create intermediary types that can be used like entity types but aren't necessarily directly backed by the database.
As second question, how would I do Joins? This doesn't work either:
public IQueryable GetUsers()
{
return from u in DbContext.Users orderby u.UserId select new { u.UserId, u.UserName };
}
In terms of web.api it is possible to construct a return type as a dto (data transfer object) through a .Select, as you have nearly done. The little issue here is that the full statement must resolve to an expression and that the linq data provider can understand
and translate. So if you were to construct the type in the expression without it having a parameterized constructor and to use c#'s object initializer syntax instead, you should be ok for an IQueryable<ExternalUser> with web.api.
It appears quite clever at this, as the odata filter will also works against your return type ok.
So do .Select(u => new ExternalUser { UserId = u.Userid, UserName = u.UserName });
(the inline construction could be re-factored out to a method but this should return not the class but rather the expression instead)
Interestingly you can also navigate down to other properties of u if you want to change the shape, so CompanyName = u.Company.Name and also construct and return a list of entity associations of u too, as you would, implicitly with linq for a join.
Sorry I don't know about the second part and whether it will work with DbDataController specifically. I've not tried this perhaps someone else can comment. I'd be really interested to know.
I get the same error when I build a DTO as you are trying to do.
I think we have to rewrite all the DbDataController to be able to use DTO because DbDataController rebuilds,checks, validates only entity types.The team forgot the DTO layer in my opinion: it is easier to avoid mapping DTO to EF and remap EF to DTO.
This is a big problem because for now mvc4 Single page application is only a database manager !!
That was what I was thinking because DbDataController has a generic type of the data context. DbDataController is giving you more functionality from the base class, so I guess you have to work with it as there may be certain assumptions the client may be
making, but obviously if you want to go the Dto route web api is the choice. I agree, it appears like a trade-off at first glance, perhaps someone else knows more, it is in beta too.
Just had a thought. In upshot there is a provider concept to abstract the service layer and I believe at least a couple, odata and ria (this) at present.
That error message is coming from the base DataController, not the DbDataController, so by "must return an entity type" it doesn't mean that the object must be an EF entity. It just has to have a primary key (by definition in DataController any object that
has a primary key is an entity).
If you decorate the key of your ExternalUser with the [Key] attribute then it should work.
That's helpful Colin and worth trying out. So does that mean Eric could do what he is wanting and provide a Dto layer that abstracts the underlying EF data provider, if he so wanted, which would also extend to Create and Update operations too with DataController,
albeit with appropriate mappings?
The Presentation Model
documentation for the DomainService might help you understand some of the concepts.
Actually, you may be able to gather a lot of information from reading the
Middle Tier documentation of WCF RIA Services. There is not a 1:1 relationship between DomainService and DataController but many of the same ideas are involved.
ColinBlair gives the answer "as usual": I add the [Key] metadata and it works now.
With Ria I have to do like this to remap to the DTO after inserting, but how to process this.ChangeSet.Associate with spa because I can't use InsertEntity to do the job. Am I right ?
1: publicvoid InsertUser(UserDto user)
2: {
3: User newUser = new User { FirstName = user.FirstName, LastName = user.LastName };
It looks like ChangeSet.Associate itself is missing from the DataController version of ChangeSet so it looks like you will need to manually keep track of how the DTO and actual entity connect to each other instead of letting the ChangeSet handle it. Not
sure if that is a permament thing or they just didn't get the PM feature into the DataController before the initial public release.
EricLou
0 Points
1 Post
Is there a workaround the requirement that entity types should be exposed from the DBDataControll...
Mar 14, 2012 03:21 PM|LINK
Here's what I would like to do:
I want to return something like this from the DBDataController to be consumed by upshot.
public IQueryable<ExternalUser> GetUsers()
{
return DbContext.Users.OrderBy(u => u.UserId).Select(x => new ExternalUser(x));
}
Where Users is of type User:
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
And where ExternalUser is:
public class ExternalUser
{
Models.User user;
public ExternalUser(Models.User user)
{
this.user = user;
}
public int UserId { get { return user.UserId; } set { user.UserId = value; } }
public string UserName { get { return user.UserName; } set { user.UserName = value; } }
}
The objective is to put a lot of business logic in those intermediate Entities. For security, performance and elegance reasons there are always things in the database that you want exposed to your views in a slightly or sometime radically different structure.
But when I try to run that code I get an error message: "queryOperation '{0}' must return an entity type or an IEnumerable/IQueryable of an entity type"
So I'd like to be able to easily create intermediary types that can be used like entity types but aren't necessarily directly backed by the database.
As second question, how would I do Joins? This doesn't work either:
public IQueryable GetUsers()
{
return from u in DbContext.Users orderby u.UserId select new { u.UserId, u.UserName };
}
benaw
Member
211 Points
95 Posts
Re: Is there a workaround the requirement that entity types should be exposed from the DBDataCont...
Mar 14, 2012 06:34 PM|LINK
In terms of web.api it is possible to construct a return type as a dto (data transfer object) through a .Select, as you have nearly done. The little issue here is that the full statement must resolve to an expression and that the linq data provider can understand and translate. So if you were to construct the type in the expression without it having a parameterized constructor and to use c#'s object initializer syntax instead, you should be ok for an IQueryable<ExternalUser> with web.api.
It appears quite clever at this, as the odata filter will also works against your return type ok.
So do .Select(u => new ExternalUser { UserId = u.Userid, UserName = u.UserName });
(the inline construction could be re-factored out to a method but this should return not the class but rather the expression instead)
Interestingly you can also navigate down to other properties of u if you want to change the shape, so CompanyName = u.Company.Name and also construct and return a list of entity associations of u too, as you would, implicitly with linq for a join.
Sorry I don't know about the second part and whether it will work with DbDataController specifically. I've not tried this perhaps someone else can comment. I'd be really interested to know.
Alphapage
Member
25 Points
24 Posts
Re: Is there a workaround the requirement that entity types should be exposed from the DBDataCont...
Mar 14, 2012 06:35 PM|LINK
Hello,
I get the same error when I build a DTO as you are trying to do.
I think we have to rewrite all the DbDataController to be able to use DTO because DbDataController rebuilds,checks, validates only entity types.The team forgot the DTO layer in my opinion: it is easier to avoid mapping DTO to EF and remap EF to DTO.
This is a big problem because for now mvc4 Single page application is only a database manager !!
This is not an answer,just a comment.
benaw
Member
211 Points
95 Posts
Re: Is there a workaround the requirement that entity types should be exposed from the DBDataCont...
Mar 14, 2012 06:48 PM|LINK
That was what I was thinking because DbDataController has a generic type of the data context. DbDataController is giving you more functionality from the base class, so I guess you have to work with it as there may be certain assumptions the client may be making, but obviously if you want to go the Dto route web api is the choice. I agree, it appears like a trade-off at first glance, perhaps someone else knows more, it is in beta too.
Just had a thought. In upshot there is a provider concept to abstract the service layer and I believe at least a couple, odata and ria (this) at present.
ColinBlair
Member
146 Points
36 Posts
Re: Is there a workaround the requirement that entity types should be exposed from the DBDataCont...
Mar 14, 2012 07:01 PM|LINK
That error message is coming from the base DataController, not the DbDataController, so by "must return an entity type" it doesn't mean that the object must be an EF entity. It just has to have a primary key (by definition in DataController any object that has a primary key is an entity).
If you decorate the key of your ExternalUser with the [Key] attribute then it should work.
Upshot Blog
ColinBlair on Twitter
MVVM and RIA Services
benaw
Member
211 Points
95 Posts
Re: Is there a workaround the requirement that entity types should be exposed from the DBDataCont...
Mar 14, 2012 07:31 PM|LINK
That's helpful Colin and worth trying out. So does that mean Eric could do what he is wanting and provide a Dto layer that abstracts the underlying EF data provider, if he so wanted, which would also extend to Create and Update operations too with DataController, albeit with appropriate mappings?
ColinBlair
Member
146 Points
36 Posts
Re: Is there a workaround the requirement that entity types should be exposed from the DBDataCont...
Mar 14, 2012 07:49 PM|LINK
Yes.
The Presentation Model documentation for the DomainService might help you understand some of the concepts.
Actually, you may be able to gather a lot of information from reading the Middle Tier documentation of WCF RIA Services. There is not a 1:1 relationship between DomainService and DataController but many of the same ideas are involved.
Upshot Blog
ColinBlair on Twitter
MVVM and RIA Services
Alphapage
Member
25 Points
24 Posts
Re: Is there a workaround the requirement that entity types should be exposed from the DBDataCont...
Mar 14, 2012 08:02 PM|LINK
ColinBlair gives the answer "as usual": I add the [Key] metadata and it works now.
With Ria I have to do like this to remap to the DTO after inserting, but how to process this.ChangeSet.Associate with spa because I can't use InsertEntity to do the job. Am I right ?
1: public void InsertUser(UserDto user)
2: {6: }7:9: {10: user.Id = dalUser.Id;11: }
ColinBlair
Member
146 Points
36 Posts
Re: Is there a workaround the requirement that entity types should be exposed from the DBDataCont...
Mar 14, 2012 09:03 PM|LINK
It looks like ChangeSet.Associate itself is missing from the DataController version of ChangeSet so it looks like you will need to manually keep track of how the DTO and actual entity connect to each other instead of letting the ChangeSet handle it. Not sure if that is a permament thing or they just didn't get the PM feature into the DataController before the initial public release.
One thing I can tell you, DeleteEntity, InsertEntity, and UpdateEntity are more like helper classes than something you actually have to use. Internally, they are like the code you can see Remco using at http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/57793bec-abc6-4520-ac1d-a63e40239aed/ with the DbDomainService. If you want to just update the DbContext directly the way that you would have using the DbDomainService feel free to do so using pretty much the same code as was shown at http://varunpuranik.wordpress.com/2011/06/29/wcf-ria-services-support-for-ef-4-1-and-ef-code-first/.
Upshot Blog
ColinBlair on Twitter
MVVM and RIA Services