I have a table like that,
public class Asset
{
public int id_asset { get; set; }
public int id_user { get; set; }
public int id_parent_asset { get; set; }
public string name { get; set; }
public virtual Asset Parent_Asset { get; set; }
public virtual ICollection<Asset> Child_Assets { get; set; }
public virtual User User { get; set; }
}
I want to get all children assets of an asset. I tried following code,
context.Asset.Where(o => o.name.Contains("Root")).Include(o => o.Child_Assets ).FirstOrDefault();
But it just contains one level children. I need to get all levels. I don't know that how many levels I should get so I need a recursive method or maybe there is already a method that is doing that.
Is there anyone who knows how can I handle this issue.
I had the same problem and was able to solve it using Lazy Loading instead of Eager loading. That may or may not be suitable for you.
With Lazy Loading you don't need to specify which navigation properties to load when you query the database (No need for the .Include calls). EF Core will automagically load in the related entities for you as you access them. ie get the root asset from the context and it's children will be there.
To use lazy loading you need to add Microsoft.EntityFrameworkCore.Proxies nuget package and then call .UseLazyLoadingProxies() when you configure the context. See the Docs for details https://learn.microsoft.com/en-us/ef/core/querying/related-data#lazy-loading
Also note that for the magic to happen the context that loaded the entity needs to know about the related entities (the child assets in your case). It won't go query the database for the child assets but if it has them in memory it will link them up. So in your case you will need to load all the assets then get the root asset and its children and their children will be there.
If you are using multiple contexts you can run into issues. Say the assets are loaded into one context and the users into another context, in that case root.User will be null because the context used to load the root asset doesn't have the users in memory.
Related
I am trying to build a UI using Avalonia. I have been plowing through it, but I hit a snag. This is the example code they provide:
using ReactiveUI;
public class MyViewModel : ReactiveObject
{
private string caption;
public string Caption
{
get => caption;
set => this.RaiseAndSetIfChanged(ref caption, value);
}
}
I need to set properties to "RaiseAndSetIfChanged", which require setting the get; set; of the variable within the class. I'm not sure how to do this in Powershell.
I have tried things like this:
Powershell class implement get set property
To no avail, but maybe I wasn't doing it right.
Alternatively, if there would be a different way to set RaiseAndSetIfChanged after [MyViewModel]::New() was called
Link to Example:
https://avaloniaui.net/docs/binding/change-notifications
I figured out how to use ReactiveUI in a different way, I give examples in the readme in this fork I have created of the module of Avalonia for Powershell.
https://github.com/MaynardMiner/psavalonia
It has a method called RaisePropertyChanged. I used it as a workaround to update bindings.
There is no possible way to natively create a getter/setter for C# emulation after searching.
The question could also look like "Why is my initialization code of object in server not working?".
For example,
public class Order
{
public int Id { get; set; }
public int Quantity { get; set; }
public Order()
{
Quantity = 10;
}
}
From debugger, I can see the contructor is called and Quantity is set, however, it is not taking effect. I have to set Quantity on client side after the entity is created to make it work.
Is there a way to make the initialization on server work? By the way, my project is in Angular/Breeze/EF.
UPDATE: As I dig a little further, I believe, this is the general "issue" with Breeze that the server side change must be added to so-called entityInfo.OriginalValueMap, otherwise, its change is not saved. If true, how can work around this limitation because I have a lot default values I'd like to set on server?
This is how I create my entity:
var manager = new breeze.EntityManager("breeze/breeze");
manager.enableSaveQueuing(true);
function _createEntity(entityName) {
return manager.createEntity(entityName);
}
Setting any initialization code on the server in the model constructor won't work simply because the JavaScript client doesn't know anything about the C# constructor code on the server.
The DefaultValueAttribute is only honored when you're constructing a Model-First metadata. It is unfortunately ignored by EF when constructing a Code-First model metadata.
I suggest that you see Breeze - Create Entity on Server side for how another user solves a similar situation by creating a "create Endpoint" on the server that basically returns a new entity with default values set.
You don't have to create a constructor to set default values.
Just add the default value data annotation to any property you wish to set its default value:
[DefaultValue(10)]
public int Quantity { get; set; }
Also, consider not to initialize the Quantity when creating an entity at the client side.
I have published a WebAPI service which returns a list of items. I am implementing Breeze and have managed to get it basically working with filtering/sorting. However, the Expand is not working.
http://www.ftter.com/desktopmodules/framework/api/dare/dares?$expand=ToUser
You can see the ToUserId ForeignKey in the response above, but the ToUser properties are NULL (the user definitely exists)
You can see the ToUser EF navigation property in the metadata.
When I use .Include on the server side I can populate it with EF, but I don't want to do this.
I checked the Breeze Tutorial 2 here on Expand: http://learn.breezejs.com/
Here is it without expand: http://learn.breezejs.com/api/northwind/Products
and here it is with Expand (And you can see the additional Category info): http://learn.breezejs.com/api/northwind/Products?$expand=Category
This is what I am trying to do but mine does not fill it...
UPDATE:
I downloaded the Breeze 1.3.6 Samples and loaded the DocCode solution in VS2011.
I ran it and saw that the client-side expand works;
e.g.
http://localhost:47595/breeze/Northwind/Orders?$top=1 (no expand)
http://localhost:47595/breeze/Northwind/Orders?$top=1&$expand=Customer (expands customer correctly).
I checked the WebAPI controller code and it looks the same, except they use EF Code First instead of Model First. The Foreign key is decorated with a property:
Breeze Sample that Works
[ForeignKey("CustomerID")]
[InverseProperty("Orders")]
public Customer Customer {get; set;}
It just doesn't make sense... it is something to do with my WebAPI controller or EntityFramework relationship...
UPDATE 2
I downloaded the most basic ToDo Knockout Breeze sample and added this line to the ToDoItem class: public User ToUser { get; set; }
I am then able to Expand the WebAPI call with http://localhost:63030/breeze/todos/Todos?$expand=ToUser
So I have come to the conclusion that it is something to do with the fact that I am using EntityFramework DB First and not Code First. It definitely does seem possible to do in the current version of the WebAPI with Breeze and EF.
UPDATE 3
I have narrowed it down to my database, EF Database First and Code First differences, but still not identified the issue. I have changed from a Model to a Code First approach with the exact same result (ie. no expand).
For example: if you look at this Expand on the Breeze site that works, http://learn.breezejs.com/api/northwind/Products?%24expand=Category, try change the last param to an invalid field and it throws an error, e.g. :
http://learn.breezejs.com/api/northwind/Products?%24expand=Category1
However, in my code, it always ignores this param and returns ALL the records, and never throws an exception if the Expand param is incorrect:
http://www.ftter.com/desktopmodules/framework/api/dare/dares?$expand=To4657657User
Hence I am stumped.. I have no idea why this is not working.
My Code
[HttpGet]
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
public HttpResponseMessage Dares()
{
var response = Request.CreateResponse(HttpStatusCode.OK, (IQueryable<Dare>)contextProvider.Context.Dares);
return ControllerUtilities.GetResponseWithCorsHeader(response);
}
and here is the generated class from my EF model (using Database First)
public partial class Dare
{
public int DareId { get; set; }
public int ToUserId { get; set; }
public virtual User ToUser { get; set; }
}
Your URL seems to be missing the $ for the expand query option...should be $expand.
I think I have found the problem - the IQueryable with the HttpResponseMessage return type does not behave the same as a pure IQueryable return type. expand seems to work when I do not wrap it.
I have raised a new question here:
How to use Breeze IQueryable with CORS?
this is really annoying
I have something like this:
class Person {
..properties id, name etc..
}
class Task {
..properties id, name etc..
Person Moderator {get;set}
}
public class DataModel : DbContext {
public DbSet<Task> Tasks { get; set; }
public DbSet<Person> People { get; set; }
}
I can then create new tasks and add People objects to the task and save, and I can see the data correctly saved in the sql backend - each Task saved has the correct Person id saved
with it and the Person with that id is saved back as well.
But when I try and get back a task, the person object is always null.
using (DataModel db = new DataModel()) {
Task t = db.Tasks.SingleOrDefault(p => p.Id == 22);
assert(t.Name.Lenght>0)
assert(t.Moderator != null) // always null!!!!!!
....
}
What do I have to do to get the whole object graph bought back? Do I have to do a join in the SingleorDefault call? seems a bit wrong somehow.
Did I mention this is really annoying.
TIA,
Two options for you. By default the code first / dbContext model returns a proxy object that derives from your model (this is important to understand when you run into JSON serialization issues). The proxy object uses lazy loading of associations but only under certain circumstances. The Moderator property has to be declared as virtual so that the proxy can override it and do the lazy loading for you.
However lazy loading can create a problem called Select N+1. If in most cases you only need the Task and not the Moderator, this won't be a problem. However if you frequently display a list of tasks and their associated moderators, you will effectively have to run an extra round trip to the database for every task in that list in addition to the 1 for the original list(e.g. for a list of 100 tasks you would do 101 queries to display the tasks and their moderators).
To get around this, EF provides the Include operator, this forces the relation to load. Use it as such
Task t = db.Tasks.Include(t=>t.Moderator).SingleOrDefault(p => p.Id ==
22);
Hope this helps.
You have lazy loading turned off for your Moderator property, so it will only be loaded if you explicitly do so using Load().
You can force EF to eagerly load your related Person entity by using the Include() method in your query like this:
Task t = db.Tasks.Include(x => x.Moderator).SingleOrDefault(p => p.Id == 22)
There is a pretty good overview in this article.
I am using DB First EF 4.1 and I am adding DbContextGenerator tt template to my model. This is all great, but I end up with classes like this:
public partial class t_city
{
public t_city()
{
this.t_neighborhood = new HashSet<t_neighborhood>();
}
public int city_id { get; set; }
public string city_name { get; set; }
public virtual ICollection<t_neighborhood> t_neighborhood { get; set; }
}
This is super ugly. I modified the template to generate properties in camelcase, but that breaks the mapping onto tables and columns. Is there way to get clean class names and still preserve the mapping?
EDIT
Looks like it's possible by renaming the objects inside the Entity Model file. The only question remains, is it possible to automate the renaming using a function, or does it have to be done manually each time?
Thanks!
You need to do it manually but that's needed only once for each entity / property. These changes are not deleted when you update your model from the database.
The only automation can be implemented as some processing of EDMX file. It is XML with defined schema so you can process that XML in your custom tool or XSLT transformation and automatically change property and entity names in CSDL and MSL.