Loading recursive OData (WCF Data Service) - recursion

I have a wcf data service that has a service "Nodes" that return the following entity.
[DataServiceKey("NodeId")]
public class Node
{
public int NodeId { get; set; }
public IQueryable<Node> SubNodes
{
get { return new NodeRepository().GetNodes(this.NodeId); }
}
}
which gives me a fully recursive OData service. I get all root nodes with http://www.test.com/api/Nodes, a single node with http://www.test.com/api/Nodes(123), it's subnodes with http://www.test.com/api/Nodes(123)/SubNodes, a single subnode with http://www.test.com/api/Nodes(123)/SubNodes(234), the subnode's subnodes with http://www.test.com/api/Nodes(123)/SubNodes(234)/SubNodes and so on.
The question is how to consume this data in code.
I can use
var context = new MyServiceV1.TheServiceContext(new Uri(dataUrl));
var nodes = context.Nodes;
to get the first level of nodes. Then if I pass the context and my nodes to this method I can get the subnodes of the first level nodes.
private void RecurseNodes(TheServiceContext context, IEnumerable<Node> nodes)
{
foreach (var node in nodes)
{
var subNodes = dataContext.LoadProperty(node, "SubNodes") as QueryOperationResponse<Node>;
RecurseNodes(dataContext, subNodes);
}
}
The above LoadProperty call will generate the correct url (/api/Nodes(123)/SubNodes) but when it tries to load the subnodes of the next level (i.e. the node with id 234) it uses the url /api/Nodes(234)/SubNodes. What it should use is /api/Nodes(123)/SubNodes(234)/SubNodes.
Do I have to start generating the urls myself or can I instruct the datacontext to accomplish this?
SOLUTION: The problem is in the OData-feed, since /api/Nodes does not return all Node's, only the root nodes. This is how I solved it without touching the OData-feed:
private void RecurseNodes(IEnumerable<Node> nodes, NCContentServiceContext dataContext, string baseUrl)
{
foreach (var node in nodes)
{
var url = baseUrl + "(" + node.NodeId + ")/SubNodes";
var subNodes = dataContext.Execute<Node>(new Uri(url, UriKind.Relative)).ToList();
RecurseNodes(subNodes, dataContext, url);
}
}
and the caller is
var context = new MyServiceV1.TheServiceContext(new Uri(dataUrl));
RecurseNodes(context.Nodes, context, "/Nodes");

I'm not sure why you need the recursive URI, but the URL here generated by LoadProperty() is correct from an OData perspective.
In OData world, every entity has a unique entity id. For instance, /api/Nodes(123) and /api/Nodes(234) are the entity ids of the two entities. So the query /api/Nodes(123)/SubNodes(234) is really reaching for the entity /api/Nodes(234). And when you ask for /api/Nodes(123)/SubNodes(234)/SubNodes, it's the same as /api/Nodes(234)/SubNodes. So LoadProperty is doing the right thing here.
If you still need to build the recursive URI, you'll have to build it yourself.

According to the OData specification /api/Nodes return all entities of type Node.
So, /api/Nodes(234) will return node #234.
You need to develope a custom method to return the root nodes.

Related

OData - $top in $expand doesn't insert "TOP" inside generated EF Query

I'm making a web service with OData + Entity Framework for my application.
After projecting members from Database Entities to Application Model (using AutoMapper ProjectTo), Entity Framework print thousands of rows instead of limiting the rows with $top attribute in OData URL.
[HttpGet]
[EnableQuery(MaxExpansionDepth = 7, EnsureStableOrdering = false)]
public virtual SingleResult<TModel> Get([FromODataUri] TKeyType key, ODataQueryOptions<TModel> opts)
{
IQueryable<TEntity> data = _db.Set<TEntity>()
.Where(_db.BuildKeyPredicate<TEntity>(key));
return SingleResult.Create(GetProjected(opts, data));
}
private IQueryable<TModel> GetProjected(ODataQueryOptions<TModel> opts, IQueryable data)
{
string[] expandedProperties = Helpers.Expand.GetMembersToExpandNames(opts);
IQueryable<TModel> projected;
if (expandedProperties.Count() > 0)
{
projected = data.ProjectTo<TModel>(null, expandedProperties);
// expandedProperties is an array of string with the names of properties to expand specified in $expand
}
else
{
projected = data.ProjectTo<TModel>();
}
return projected;
}
example URL: http://WEBSERVER/odata/Entity(416)?$expand=Child($top=100)
in the debugger, Child lenght is more than 100... in my case 57k. This is why I use $top :)
I was thinking that Entity Framework automatically translates $top parameter in OData with TOP keyword in query, but this is not happening.
If you want more code/ more documentation please tell me!
To solve this instead of using $expand you can use a Related Entity Route like this:
http://WEBSERVER/odata/Entity(416)/Child?$top=100
In order to make route works you need to implement inside the EntityController a GetChild action like this:
[EnableQuery]
[ODataRoute("({key})/Child")]
public IQueryable<Child> GetChild([FromODataUri] int key)
{
return db.Childs.Where(m => m.EntityId == key);
}
For more information about OData v4 Related Entities visit this Entity Relations in OData v4

ASP.NET, thread and connection-string stored in Session

My application can connect with multiple data bases (every data base have the same schema), I store the current DB, selected by user, in Session and encapsule access using a static property like:
public class DataBase
{
public static string CurrentDB
{
get
{
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
HttpContext.Current.Session["CurrentDB"] = value;
}
}
}
Other pieces of code access the static CurrentDB to determine what DB use.
Some actions start background process in a thread and it need access the CurrentDB to do some stuff. I'm thinking using something like this:
[ThreadStatic]
private static string _threadSafeCurrentDB;
public static string CurrentDB
{
get
{
if (HttpContext.Current == null)
return _threadSafeCurrentDB;
return HttpContext.Current.Session["CurrentDB"].ToString();
}
set
{
if (HttpContext.Current == null)
_threadSafeCurrentDB = value;
else
HttpContext.Current.Session["CurrentDB"] = value;
}
}
And start thread like:
public class MyThread
{
private string _currentDB;
private thread _thread;
public MyThread (string currentDB)
{
_currentDB = currentDB;
_thread = new Thread(DoWork);
}
public DoWork ()
{
DataBase.CurrentDB = _currentDB;
... //Do the work
}
}
This is a bad practice?
Actually, I think you should be able to determine which thread uses which database, so I would create a class inherited from Thread, but aware of the database it uses. It should have a getDB() method, so, if you need a new Thread which will use the same database as used in another specific Thread, you can use it. You should be able to setDB(db) of a Thread as well.
In the session you are using a current DB approach, which assumes that there is a single current DB. If this assumption describes the truth, then you can leave it as it is and update it whenever a new current DB is being used. If you have to use several databases in the same time, then you might want to have a Dictionary of databases, where the Value would be the DB and the Key would be some kind of code which would have a sematic meaning which you could use to be able to determine which instance is needed where.

asp.net web API HTTP PUT method

I have some resource- UserProfile
public UserProfile
{
public string Email{get;set;}
public string Password{get;set;}
}
I want to change Email and Password separatly (only one for user at same time). I have web api controller for example /api/user/123 that handle requests in RESTful style. Follow the RESTful style i should have one method PUT which update the resource, but i have two task that update the same resource api/user/123. I need to add some feature to PUT request body like
{email:'test#domain.com', changeType:'email'} or {password:'12345678',changeType:'password' } to write some if in my PUT method ? Or there is some other way to update my resource in RESTful style ?
[HttpPut]
public HttpResponseMessage PutProduct(Product p)
{
Product pro = _products.Find(pr => pr.Id == p.Id);
if (pro == null)
return new HttpResponseMessage(HttpStatusCode.NotFound);
pro.Id = p.Id;
pro.Name = p.Name;
pro.Description = p.Description;
return new HttpResponseMessage(HttpStatusCode.OK);
}
You have two options for updating email and password separately.
A) Don't use PUT, use POST
B) Create child resources for updating the individual elements, e.g.
PUT /api/user/123/email
And
PUT /api/user/123/password

SignalR - Leave All Groups

Using a SignalR hub clients can be added or removed from a group. A client can belong to multiple groups. Is it possible to remove a client from every group it currently belongs to? I guess what I'm looking for is something like Clients[*allgroups*].leave(Context.ConnectionId)
As of v0.5.2, there is no way to leave all groups because the server doesn't keep track of the groups a client belongs to. You need to do this yourself and remove the client from each group one-by-one.
There's a request for something similar in the backlog however, so maybe this will be implemented in a future release: https://github.com/SignalR/SignalR/issues/66
Looks like they have yet to implement this, but it is considered a candidate for v3. A feature request with the following code exists at https://github.com/SignalR/SignalR/issues/66
public static class SignalRConnectionToGroupsMap
{
private static readonly ConcurrentDictionary<string, List<string>> Map = new ConcurrentDictionary<string, List<string>>();
public static bool TryAddGroup(string connectionId, string groupName)
{
List<string> groups;
if (!Map.TryGetValue(connectionId, out groups))
{
return Map.TryAdd(connectionId, new List<string>() {groupName});
}
if (!groups.Contains(groupName))
{
groups.Add(groupName);
}
return true;
}
// since for this use case we will only want to get the List of group names
// when we're removing the mapping - we might as well remove the mapping while
// we're grabbing the List
public static bool TryRemoveConnection(string connectionId, out List<string> result)
{
return Map.TryRemove(connectionId, out result);
}
}

AutoMapper pass parent reference when mapping child object

I am trying to use AutoMapper to map some DTO (data contract) objects received from a web service into my business objects. The root DTO object contains a collection of child objects. My business object also has a child collection of child business objects. In order to get AutoMapper to work, I had to include a setter on the collection property in my business object or the collection would always be empty. In addition, I had to add a default constructor to the collection type. So, it appears to me that AutoMapper is instantiating a new collection object, populating it and setting as the collection property of my business object.
While this is all well and good, I have additional logic that has to be wired up when the collection is created and having the default constructor defeats the purpose. Essentially, I am establishing the parent-child relationship and wiring up some events so they bubble from child to parent.
What I would like to do is to have AutoMapper simply map the child objects from the DTO's collection to the existing collection on my BO. In other words, skip creating a new collection and simply use the one the business object already has.
Is there any way to easily accomplish this?!?!?
UPDATE
Perhaps a better question, and simpler solution to my problem, is if it is possible to define arguments that AutoMapper will pass to the collection when instantiated? My child collection is defined like this:
public class ChildCollection : Collection<ChildObjects>
{
public ChildCollection(ParentObject parent) { Parent = parent; }
}
If I can configure AutoMapper to use this constructor and pass in the proper object, that would be PERFECT!
ANOTHER UPDATE
For the sake of clarity, here are the other classes in the problem space:
public class ParentObject
{
private ChildCollection _children;
public ChildCollection Children
{
get
{
if (_children == null) _children = new ChildCollection(this);
return _children;
}
}
}
public class ParentDTO
{
public ICollection<ChildDTO> Children { get; set; }
}
public class ChildDTO
{
public String SomeProperty { get; set; }
}
I configure AutoMapper this way:
Mapper.CreateMap<ParentDTO, ParentObject>();
Mapper.CreateMap<ChildDTO, ChildObject>();
Doing so this way and I have to add a setter to the Children property in ParentObject and a default (parameterless) constructor to ChildCollection. While I can work around the need to define the parent-child relationship, it seems that it would be logical to expect AutoMapper to support configuring the map to use a specific constructor when creating the child collection. Something like this:
Mapper.CreateMap<ParentDTO, ParentObject>()
.ForMember(obj => obj.Children, opt.MapFrom(dto => dto.Children))
.ConstructUsing(col => new ChildCollection(obj));
Notice that I am passing in the reference to "obj" which is the ParentObject instance being mapped.
It turns out that the answer was right there all along. The UseDestinationValue option does exactly what I want.
This options instructs AutoMapper to use the existing property on the target object and map any child properties or collection items into that object rather than creating a new proxy object.
So, here's all I have to do in my application:
Mapper.CreateMap<ParentDTO, ParentObject>()
.ForMember(obj => obj.Children,
opt.UseDestinationValue());
And, voila! Now I can instantiate the child collection, with parent reference, and setup the reference back to the parent in each item as it is added to the collection.
If I understood your problem, you should be able to use ConstructUsing as stated in this answer:
Automapper - how to map to constructor parameters instead of property setters
Its possible for EntityFramework (mvc or core) with AutoMapper.Collection.EntityFrameworkCore or EntityFramework by matching child ids to destination object ids like below
_config = new MapperConfiguration(cfg =>
{
cfg.AddCollectionMappers();
cfg.CreateMap<Order, OrderDto>()
.EqualityComparison((odto, o) => odto.Id == o.Id)
.ReverseMap();
cfg.CreateMap<OrderDetail, OrderDetailDto>().EqualityComparison((odto, o) => odto.Id == o.Id).ReverseMap();
});
In my case i have a List<Order> and one of my order contains a list of items such as List<OrderDetails> if i want to map List<Order> to List<OrderDto> with List<OrderDetailDto> that is not possible to mapping configuration. But i done it by seperating them like above. And i can use it like below
public int AddMany(List<OrderDto> orderDtos)
{
try
{
List<Order> orders = new List<Order>();
foreach (var oi in orderDtos)
{
var oneOrder = _mapper.Map<OrderDto, Order>(oi);
oneOrder.OrderDetails = new List<OrderDetail>();
foreach (var oid in oi.OrderDetails)
{
var oneOrderItem = _mapper.Map<OrderDetailDto, OrderDetail>(oid);
oneOrder.OrderDetails.Add(oneOrderItem);
}
orders.Add(oneOrder);
}
_orderRepository.InsertMany(orders);
return _uow.SaveChangesAsync().Result;
}
catch (Exception ex)
{
return 0;
}
}
There would be a manner to configuring it from the top but its very difficult, i couldn't find it now. But this maybe a resulation for someone.
Also in my opinion Mapster powerfull than autofac for those mapping configurations.

Resources