I'm having an impossible time figuring out why my returned child entities are null for a particular query:
The item has a child property with a 0 or 1 multiplicity called MetaDataItem.
var res = ObjectContext.Contents.Include("MetaDataItem");
This works. I can confirm that res contains 1 instance of MetaDataItem.
On the client:
var loadOperation = _cContext.Load(_cContext.GetItemQuery(itemId.Value)....
Content item = _cContext.Contents.First(c => c.ContentId == itemId.Value);
Here item.MetaDataItem is null instead of containing the one instance.
Here is the metadata class (note that I DO have an include attribute):
[MetadataTypeAttribute(typeof(Content.ContentMetadata))]
public partial class Content
{
internal sealed class ContentMetadata
{
[Include]
public MetaDataItem MetaDataItem { get; set; }
}
}
So, why isn't my object populating? What else should I be looking at? Unfortunately, this appears to be where the magic of RIA services occurs, so I can't even really get a handle on attempting to debug the issue. Any help would be most appreciated.
Related
I'm using ASP.NET Web API 2 for the backend and AngularJS for the frontend. I want to display a treeview in AngularJS.
So I need a nested Model in my ASP Web API. The model currently looks like this:
public class Register
{
public int ID { get; set; }
public string Name { get; set; }
public List<Register> Children { get; set; }
}
and the controller like this:
// GET: api/Registers
public IQueryable<Register> GetRegister()
{
return db.Register;
}
In my Database I added following examples:
The JSON Output shows:
[{"ID":4,"Name":"RootFolder","Children":[{"ID":5,"Name":"Sub1","Children":null}]},{"ID":5,"Name":"Sub1","Children":null}]
The first entry looks fine, but I don't want to output the children itself. It it possible to only output the RootFolder with its Children and not the children itself? Like this:
{"ID":4,"Name":"RootFolder","Children":[{"ID":5,"Name":"Sub1","Children":null}]}
The second problem is, when I want enter a specific Folder ID (For example ID 4(RootFolder)). I only get this as a response:
Why there are no child elements? The controller is this one:
// GET: api/Registers/5
[ResponseType(typeof(Register))]
public IHttpActionResult GetRegister(int id)
{
Register register = db.Register.Find(id);
if (register == null)
{
return NotFound();
}
return Ok(register);
}
Summary:
How can I filter the Output to only show the RootFolders with each children but not showing the children objects itself?
Why there are no children when I specify a specific ID?
I guess you are using entity framework for database operations.
For the first point, you need to filter root folder row e.g. for root Register_ID is null
db.Register.Where(x => x.Register_ID == null && x.ID==ID).ToList()
For second point to load root folder's children you can include it's navigation property, for example refer below code.
db.Register.Include(x => x.Children).Where(x => x.Register_ID == null).ToList();
I access data in a database through a Web-Api and send it back as Json string to an UWP-App.
MessageRepository.cs:
public List<Message> GetByRecipientId(int id)
{
var listOfMsg = new List<Message>();
using (var ctx = new ShowcaseContext())
{
var recipientWithId = ctx.Recipients.Where(rec => rec.UserId == id);
foreach (var recipient in recipientWithId.ToList())
{
var msgWithId = ctx.Messages.Where(msg => msg.Id == recipient.MessageId);
listOfMsg.Add(msgWithId.Single());
}
return listOfMsg;
}
}
MessageController.cs:
[Route("api/message/recipient/{id}")]
public IEnumerable<Message> GetByRecipientId(int id)
{
return messageRepository.GetByRecipientId(id);
}
The problem is that I'm getting an
"Newtonsoft.Json.JsonSerializationException" Error
when trying to return listOfMsg to the UWP-App.
But if I look at the List<> and it items in debug mode, or access a specific message in the List<> before returning it, the error disappeares and I get the right data in the UWP-App.
It seems like the List<> has a different format before accessing it specifically which can't be serialized.
Has anyone an idea why?
Edit
Message.cs:
public class Message
{
public int Id { get; set; }
public virtual Collection<Recipient> Recipients { get; set; }
public MessageTrigger Trigger { get; set; }
public string Body { get; set; }
public DateTime CreatedTs { get; set; }
}
The StackTrace is null, but the error message is
The "ObjectContent`1" type failed to serialize the response body for content type "application/json; charset=utf-8".
Most probably you have lazy loading enabled. That means that when you access a navigation property, EF will try to hit the database to get the related entity, and this will work if the DbContext is still "alive".
After the API call returns the data, JSON.NET will visit all the properties to serialize them, including the navigation properties. And, at that point, the DbContext has already been disposed of. That's why you get your error.
To avoid it you can:
disable lay loading for a particular query: Entity Framework: How to disable lazy loading for specific query?
completely disable it, if you're not using it: Disable lazy loading by default in Entity Framework 4
For more info on disabling lazy loading.
Alternative: Json configuration
You can also instruct JSON.NET to skip some properties. But there are mabny ways of doing so, and you have to add attributes to your entities. For example:
JsonObjectAttribute opt-in serialization
JsonIgnoreAttribute
Alternative: projection to anonymus class with .Select()
And I forgot a final, easy option: you can project your query to an anonymous class, getting read of all problems, and returning the JSON that you exactly want to return.
.Select(new { /* your projection */ });
Although this answers are for older versions, it still works in the same way.
NOTE: while you're debugging you're still inside the DbContext using block, and that's why you don't get errors while debugging. If you put this line: return listOfMsg; outside the using block, add a breakpoint in that line, and watch navigation properties, you'll also get errors in the debugger.
In a ASP.NET MVC 5 web site I have a GridView using the devexpress component binding using the LINQ method.
EF generated a partial class to map a table that i use to display in that gridview.
In this partial class generated by the EF i have a ID_Status property wich has a corresponding description in other table. I made another partial class to deal with this custom Property and it works ok, except when i try to make a 'Sort' operation clicking on the header of this column.
The partial class generated by the EF.
[Table("Test")]
public partial class Test
{
[Key]
public long ID_Test { get; set; }
public long ID_TestStatus { get; set; }
//other properties
}
My Custom partial class:
public partial class Test
{
private static readonly TestRepository _testRepository;
static TestRepository()
{
_testRepository= new TestRepository();
}
public string StatusDescription
{
get { return _testRepository.GetStatusDescriptionById(ID_TestStatus); }
}
}
When i try to Sort using another column it works fine, but when i try to Sort using the custom property Column all the grid cell values gets empty, without any value.
Any suggestion?
It's not a very good idea to have data access code inside an entity. One reason is that it makes it very hard to write unit test. Another reason is that it is very likely to give rise to the n + 1 anti pattern. In your case, it does: one (1) query to get the Tests, then each Test (n) sends a separate query to the database to get its StatusDescription.
The way you implemented it also raises some eyebrows, because
_testRepository is static, which meas there is probable some context instance living for the entire lifecycle of the application - unless GetStatusDescriptionById creates a new context for each call, but that wouldn't be a good idea either.
The GetStatusDescriptionById call is made each time the property is accessed. In a web application this may not be a big problem because the objects are newly created each time they are requested anyway, but in other environments this could be highly inefficient.
A better approach would be to fetch the Testss with their Status included:
context.Tests.Include(t => t.TestStatus)
and have an unmapped property like
public string StatusDescription
{
get { return TestStatus== null ? string.Empty : TestStatus.Description; }
}
better still (in my opinion) would be not to show Test objects directly, but TestDto objects like
public class TestDto
{
public string StatusDescription { get; set; }
//other properties that match Test's properties
}
and use a tool like AutoMapper to map a collection of Tests to TestDtos. If Test has a property Status and TestStatus has a property Description, AutoMapper will be able to flatten that into StatusDescription automatically.
Both this StatusDescription property and the Dto appraoch set the state of a Test(Dto) object once. I don't think any grid component can mess with that.
I know that "join" is not supported on client side with WCF DS thats why i decided to add a method on server side to do the "join" and return the result as custom type object.
Service looks like this:
public class CWcfDataService : DataService<CEntities>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
config.UseVerboseErrors = true;
config.RegisterKnownType(typeof(CASE_STAGE_HISTORY_EXTENDED));
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
[WebGet]
public IQueryable<CASE_STAGE_HISTORY_EXTENDED> GetCASE_STAGE_HISTORY_EXTENDEDByDocId(int docId)
{
CEntities context = new CEntities();
return (from c in context.CASE_STAGE_HISTORY
join a in context.USRs on c.CREATOR_ID equals a.USRID
select new CASE_STAGE_HISTORY_EXTENDED()
{
CASE_STAGE_ID = c.CASE_STAGE_HISTORY_ID,
CASE_STAGE_NAME = c.CASE_STAGE_NAME,
CREATE_DATE = c.CREATE_DATE,
CREATOR_ID = c.CREATOR_ID,
DOC_ID = c.DOC_ID,
LAST_VARIANT_DOCUMENT_ID = c.LAST_VARIANT_DOCUEMENT_ID,
CREATOR_FULLNAME = a.FULLNAME
});
}
}
And custom class is:
[DataServiceKey("CASE_STAGE_ID")]
public class CASE_STAGE_HISTORY_EXTENDED
{
public int CASE_STAGE_ID { get; set; }
public int DOC_ID { get; set; }
public string CASE_STAGE_NAME { get; set; }
public int? LAST_VARIANT_DOCUMENT_ID { get; set; }
public DateTime? CREATE_DATE { get; set; }
public int? CREATOR_ID { get; set; }
public string CREATOR_FULLNAME { get; set; }
}
When i try to update service reference in Visual Studio i constantly get error:
The server encountered an error
processing the request. The exception
message is 'Unable to load metadata
for return type
'System.Linq.IQueryable1[CWcf.Code.CASE_STAGE_HISTORY_EXTENDED]'
of method
'System.Linq.IQueryable1[CWcf.Code.CASE_STAGE_HISTORY_EXTENDED]
GetCASE_STAGE_HISTORY_EXTENDEDByDocId(Int32)'.'.
See server logs for more details.
If i remove the public IQueryable<CASE_STAGE_HISTORY_EXTENDED> GetCASE_STAGE_HISTORY_EXTENDEDByDocId(int docId) part - on updating service reference i get another error:
The server encountered an error
processing the request. The exception
message is 'Internal Server Error. The
type
'CourtWcf.Code.CASE_STAGE_HISTORY_EXTENDED'
is not a complex type or an entity
type.'.
Environment: Visual Studio 2010, .NET 4.
I assume the data service is based on an Entity Framework model (the CEntities class is an ObjectContext).
If that's the case the types are completely read from the EF model (CSDL) and the class definitions are more or less ignored. So you need to define the type returned by your service operation in your EF model. Also note that for the IQueryable to work, that type has to be recognized by EF as an entity type (which might require mapping to the database, or something, EF experts will know more).
Added foreign keys and implemented LoadProperty. See the article from which i took this solution: http://thedatafarm.com/blog/data-access/the-cost-of-eager-loading-in-entity-framework/
Still if i have NO relations in database(for example i have no foreign keys)-i have another solution: create stored procedure and map it with import in your DB model. After that create complex type that will work in client too(tho you will have to access it by URI, not with lambda extensions). See example here. Many thanks for other answerers in this thread, you have made me look deeper into the subject.
For starters, WCF does not support IQueryable. That's your problem right there.
In your case IEnumerable should work.
You should look at a service as something that has methods and returns "data". This data could be single values, object instances or collections of objects. The client should not have to think about call a service to do a join, but rather, "give me the data for such and such - given these parameters".
You method name imparts the correct "intent" GetCaseStageHistoryExtendedByDocId(int docId), and what you get back is a collection of CASE_STAGE_HISTORY_EXTENDED objects. That's it.
An IQueryable implies something completely different and that concept does not pertain to a service as such.
EDIT
Try converting your Queryable to a List by calling the ToList() method on it.
return (from c in context.CASE_STAGE_HISTORY
join a in context.USRs on c.CREATOR_ID equals a.USRID
select new CASE_STAGE_HISTORY_EXTENDED()
{
CASE_STAGE_ID = c.CASE_STAGE_HISTORY_ID,
CASE_STAGE_NAME = c.CASE_STAGE_NAME,
CREATE_DATE = c.CREATE_DATE,
CREATOR_ID = c.CREATOR_ID,
DOC_ID = c.DOC_ID,
LAST_VARIANT_DOCUMENT_ID = c.LAST_VARIANT_DOCUEMENT_ID,
CREATOR_FULLNAME = a.FULLNAME
}).ToList();
Note that you can only send serializable objects across to the client. So first make sure your objects are serializable.
So I have an Event class which is generated from Enitity Framework, I have extended what it generated in a partial like so:
public partial class Event
{
[DataMemberAttribute()]
public EventDate CurrentEventDate { get; set; }
}
When I use the custom property ON THE SERVER it's fine, example:
[WebGet]
public IQueryable<Event> EventsOn(string date)
{
var to Return = // Get my events here
toReturn.ToList().ForEach(ev => ev.CurrentEventDate = ev.EventDates.Where(
evd => evd.StartDateTime.Date <= qDate.Date && evd.EndDateTime >= qDate.Date).Single());
return toReturn.AsQueryable();
}
But when I try and look at this object in Javascript, it tells me that:
Error: 'CurrentEventDate' is null or not an object
How can I have custom properties show up after they've been sent down the wire?
Make sure that both of the partial class definitions are in the same namespace. Otherwise the compiler will turn them into two separate classes and not warn you.
Just a long shot - EventDate seems to be a custom time. If yes then have you decorated it with Serializable and DataContract attributes (and its properties with DataMember attribute)?