I have a database table which represents accounts with a multi-level hierarchy. Each row has an "AccountKey" which represents the current account and possibly a "ParentKey" which represents the "AccountKey" of the parent.
My model class is "AccountInfo" which contains some information about the account itself, and a List of child accounts.
What's the simplest way to transform this flat database structure into a hierarchy? Can it be done directly in LINQ or do I need to loop through after the fact and build it manually?
Model
public class AccountInfo
{
public int AccountKey { get; set; }
public int? ParentKey { get; set; }
public string AccountName { get; set; }
public List<AccountInfo> Children { get; set; }
}
LINQ
var accounts =
from a in context.Accounts
select new AccountInfo
{
AccountKey = a.AccountKey,
AccountName = a.AccountName,
ParentKey = a.ParentKey
};
The structure you currently have is actually a hierarchy (an adjacency list model). The question is, do you want to keep this hierarchical model? If you do, there's a Nuget package called MVCTreeView. This package works directly with the table structure you describe - in it, you can create a Tree View for your UI, implement CRUD operations at each level, etc. I had to do exactly this and I wrote an article on CodeProject that shows how to cascade delete down an adjacency list model table in SQL via C#. If you need more specifics, leave a comment, and I'll edit this post.
http://www.codeproject.com/Tips/668199/How-to-Cascade-Delete-an-Adjace
You can simply create an association property for the parent key:
public class AccountInfo {
... // stuff you already have
public virtual AccountInfo Parent { get; set; }
}
// in the configuration (this is using Code-first configuration)
conf.HasOptional(a => a.Parent).WithMany(p => p.Children).HasForeignKey(a => a.ParentKey);
With this setup, you can traverse the hierarchy in either direction in queries or outside of queries via lazy-loading if you want lazy loading of the children, make sure to make the property virtual.
To select all children for a given parent, you might run the following query:
var children = context.Accounts
.Where(a => a.AccountKey = someKey)
.SelectMany(a => a.Children)
.ToArray();
Related
As an example let's say my database has a table with thousands of ships with every ship potentially having thousands of passengers as a navigation property:
public DbSet<Ship> Ship { get; set; }
public DbSet<Passenger> Passenger { get; set; }
public class Ship
{
public List<Passenger> passengers { get; set; }
//properties omitted for example
}
public class Passenger
{
//properties omitted for example
}
The example use case is that someone is fetching all ships per API and would like to know for each ship whether it is empty (0 passengers), so the returned JSON will contain a list of ships each with a bool whether it is empty.
My current code seems very inefficient (including all passengers just to determine if a ship is empty):
List<Ship> ships = dbContext.Ship
.Include(x => x.passengers)
.ToList();
and later when the ships are serialized to JSON:
jsonShip.isEmpty = !ship.passengers.Any();
I would like a more performant (and not bloated) alternative to including all passengers. What options do I have?
I have looked at computed columns but they only seem to support sql as string. If possible I would like to stay in the C# code world, so for example having a property which is set correctly by being automatically woven in the SQL query would be optimal.
Create a Data Transfer Object for Ship that reflects the shape of your JSON result, like -
public class ShipDto
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsEmpty { get; set; }
}
Then use projection in your query -
var ships = dbCtx.Ships
.Select(p => new ShipDto
{
Id = p.Id,
Name = p.Name,
IsEmpty = !p.Passengers.Any()
})
.ToList();
Usually, APIs need to produce responses of various shapes and DTOs give you well defined models to represent the shape of your API response. Domain entities are not always suitable for this.
If your domain entity (Ship) has a lot of properties, then copying all those properties in the .Select() method might be cumbersome. You can use AutoMapper to map them for you. AutoMapper has a ProjectTo<T>() method that can generate the SQL and return the projected result. For example, you can achieve the above result with a mapping configuration -
CreateMap<Ship, ShipDto>()
.ForMember(d => d.IsEmpty, opt => opt.MapFrom(s => !s.Passengers.Any()));
and a query -
var ships = Mapper.ProjectTo<ShipDto>(dbCtx.Ships).ToList();
assuming all other properties in ShipDto are named similar as in Ship entity.
EDIT :
If you don't want a DTO model -
you can add a NotMapped property in Ship model -
public class Ship
{
public int Id { get; set; }
public string Name { get; set; }
[NotMapped]
public bool IsEmpty { get; set; }
public List<Passenger> passengers { get; set; }
}
and then do the query like -
var ships = dbCtx.Ships
.Select(p => new Ship
{
Id = p.Id,
Name = p.Name,
IsEmpty = !p.Passengers.Any()
})
.ToList();
You can return an anonymous type -
var ships = dbCtx.Ships
.Select(p => new
{
Id = p.Id,
Name = p.Name,
IsEmpty = !p.Passengers.Any()
})
.ToList();
If I understand your intention correctly...
One way is to store the number of passengers inside each Ship entity. This can work well if you use Domain Driven Design, treat the Ship as an aggregate root, and only add or remove passengers through methods exposed on the given Ship entity, e.g. RegisterPassenger() / RemovePassenger(). Inside these methods, increment or decrement the passenger number along with adding or removing the passenger.
Then, obviously, you can query the Ships dbset with a PassengerCount < 0 projection to the bool you need. And, again obviously, it won't even touch the Passengers table.
In traditional anemic domain ASP.NET systems this sort of data redundancy might be a bit more risky, because properties are usually publicly mutable, and you have multiple services that 'massage' the entities, which is a potential source of data integrity loss.
I have an element that represents a node in a tree structure.
public class Element
{
public int Id { get; set; }
...
public Element Left { get; set; }
public Element Right { get; set; }
}
I am maintaining a table with all those elements by foreign keys to the child elements.
If I try to get this tree back by using eager loading, I get the tree in postorder traversal:
public string GetExpression(int rootId)
{
var root = _context.Set<Element>()
.Include(r => r.Left)
.Include(r => r.Right)
.ToList();
}
Is there a way using queries to get the elements in inorder traversal? Or do I have to do this by myself recursively?
The order that records are returned in a query is not defined unless you have an ORDERBY clause. So it is just luck that the example you gave returns them in a post-order.
I would suggest that you simply define in-order and post-order traversal methods and invoke them after you have loaded the entire set. You can omit the two .Include statements since EF will patch up the navigation relations during the load.
I have 2 classes like this:
Parent.cs
public class Parent
{
public int Id {get;set;}
public virtual ICollection<Child> Children { get; set; }
}
Child.cs
public class Child
{
public int Id {get;set;}
public ItemStatusType ItemStatusTyp { get; set; }
public int ParentId {get;set;}
[ForeignKey("ParentId")]
public virtual Parent Parent { get; set; }
}
ItemStatusType.cs
public enum ItemStatusType
{
Active = 1,
Deactive = 2,
Deleted = 3
}
What I want is to somehow retrieve always the active ones and not the deleted ones. Since I am not deleting the record physically, I'm merely updating the ItemStatusType to Deleted status.
So, when I say ParentObj.Children I only wish to retrieve the active ones without further using Where condition.
Here is so far what I've done but giving an exception on runtime that I stated afterwards:
public class ParentConfiguration : EntityTypeConfiguration<Parent>
{
public ParentConfiguration()
{
HasMany(c => c.Children.Where(p => p.ItemStatusTyp != ItemStatusType.Deleted).ToList())
.WithRequired(c => c.Parent)
.HasForeignKey(c => c.ParentId)
;
}
}
Runtime Exception:
The expression 'c => c.Children.Where(p => (Convert(p.ItemStatusTyp)
!= 3)).ToList()' is not a valid property expression. The expression
should represent a property: C#: 't => t.MyProperty' VB.Net:
'Function(t) t.MyProperty'.
I had to use ToList after the expression, otherwise it does not compile.
What is the proper what to do what I want?
Thanks in advance,
You cannot use Where or any other logic in fluent property mapping - that's just configuration.
Basically you cannot solve what you need in declarative way.
There are some workarounds which you can use for first-level entities, like implement your own extension(s) MySet<T> which will return .Set<T>.Where(x => x.ItemStatusType != ItemStatusType.Deleted) and use it everywhere, but that won't solve filtering issue for child collections.
You can go hard way and prepare a separate set of entities to use for selecting data, which basically should be based on database views or stored procedures; you will have to create separate view for every entity, so you will be able to combine selecting in any relations based on these views-entities.
For inserting though you will need to have entities mapped over "real" tables. No sure if it worth it but it might in some cases.
I'm developing a cross-platform app with xamarin.forms and I'm trying to look for a way to store a List of Objects directly into ElasticSearch so I can later search for results based on the objects of the lists. My scenario is the folloring:
public class Box {
[String(Index = FieldIndexOption.NotAnalyzed)]
public string id { get; set; }
public List<Category> categories { get; set; }
}
public class Category {
[String(Index = FieldIndexOption.NotAnalyzed)]
public string id { get; set; }
public string name { get; set; }
}
My aim is to be able to search for all the boxes that have a specific category.
I have tried to map everything properly like it says in the documentation but if I do it like that, when I store a box, it only stores the first category.
Is there actually a way to do it or is it just not possible with NEST?
Any tips are very welcome!
Thanks
It should just work fine with AutoMap using the code in the documentation:
If the index does not exist:
var descriptor = new CreateIndexDescriptor("indexyouwant")
.Mappings(ms => ms
.Map<Box>(m => m.AutoMap())
);
and then call something like:
await client.CreateIndexAsync(descriptor).ConfigureAwait(false);
or, when not using async:
client.CreateIndex(descriptor);
If the index already exists
Then forget about creating the CreateIndexDescriptor part above and just call:
await client.MapAsync<Box>(m => m.Index("existingindexname").AutoMap()).ConfigureAwait(false);
or, when not using async:
client.Map<Box>(m => m.Index("existingindexname").AutoMap());
Once you succesfully created a mapping for a type, you can index the documents.
Is it possible that you first had just one category in a box and mapped that to the index (Before you made it a List)? Because then you have to manually edit the mapping I guess, for example in Sense.
I don't know if you already have important data in your index but you could also delete the whole index (the mapping will be deleted too) and try it again. But then you'll lose all the documents you already indexed at the whole index.
I have database structure like this (all Parent and Childs are tables),
What I am doing
Now I want to create ad hoc reporting page, which will let user select tables and columns in these tables.
When user will select tables and columns, then they can add filters (is in list or contains or filter by etc..) to columns.
I will send all this information as json to a web service will create, then in web service I am planning to use EntityFramework to get required dataset.
What I already have done
I am able to create HTML UI, web services, database layer with repositories and UOW, database etc..
Question
I mean I can do this,
var result = context.ParentA.Include("Child1.SubChild1").Include(....).Where(..
But I am not sure how can I specify columns I want or add filters.
For Specifying Column Names you can use a select with your linq query like this :
var result = context.ParentA.Include("Child1.SubChild1").Include(....).Where(..).select(s=> new {Name = s.ParentName , SubName = s.SubChild.Name });
For adding Filters you need to define them in the where clause
.Where(p=>p.Name.contains("someValue"))
If you could create some class where you can define the filters then you can create an IQueryable<T> adding filters:
class Filters
{
public bool FilterByColumnA { get; set; }
...
public bool FilterByColumnN { get; set; }
public int FromColumnA { get; set; }
public int ToColumnA { get; set; }
...
public string FromColumnN { get; set; }
public string ToColumnN { get; set; }
}
Then you can build the query:
IQueryable<Entity> query = context.Set<Entity>();
if (filters.FilterByColumnA)
query = query.Where(e => e.ColumnA > filters.FromColumnA && e.ColumnA < filters.ToColumnA);
...
if (filters.FilterByColumnN)
query = query.Where(e => e.ColumnN > filters.FromColumnN && e.ColumnN < filters.ToColumnN);
You can see this question for more details.
To select the properties dynamically, you can choose which explicitly load using Include():
DbQuery<Entity> query = context.Set<Entity>();
foreach (var prop in properties)
query = query.Include(prop);
If the properties are declared virtual, then they will be loaded into memory only when the value is needed. If you are using OData for example, you can create select queries from the url.
Another solution may be using Expressions.
But if you really need a dynamic approach you can check this nuget package.