I am encountering some issues, using Microsooft.Data.Services.Client in a .NET Framework project, and trying to expand objects of 2nd level.
Here is an example, having following data model:
public class Customer
{
public Order Order { get; set; }
}
public class Order
{
public Item Item { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Description { get; set; }
}
If I'm using the .Execute() method, and provide a RequestUri like the following:
"V3/Customer?$expand=Order,Order/Item"
it will actually work, and do lazy loading on the sub-elements to include in my query.
If I'm using the DataServiceQuery like this (where context is an instance of DataServiceContext):
var q = context.Customers.Expand(x => x.Order).Expand(x => x.Order.Item);
This will load the Order object to the customer result, but NOT the Item of the order.
If I look on the query that the context will create, it is similar to the Uri use in the Execute.
How do I load nested elements (in this case 'Order/Item') using DataServiceContext with OData client V3?
The solution is to use projections and set the MergeOption to OverwriteChanges in this case.
I.e.
using(var ctx = new DataServiceContext(...){MergeOption = OverwriteChanges})
{
from c in ctx.Customers
select new Customer
{
Order = new Order
{
Item = new Item
{
Id = c.Order.Item.Id,
Description = c.Order.Item.Description
}
}
}
}
Related
I'm attempting to write a simple object containing a collection, for example:
public class UserModel
{
public string FirstName { get; set; }
public List<string> GroupIDs { get; set; }
}
Using a DynamicTableEntity and the Flatten method, I'm setting the properties before committing the entity by passing the UserModel object as userModel as follows:
var entity = new DynamicTableEntity(partitionKey, rowKey);
entity.Properties = TableEntity.Flatten(userModel, new OperationContext());
This inevitably returns the above mentioned error:
Parameter count mismatch
From what I can tell, this is due to the 'flatten' method as described here. I've tried setting my GroupIDs object to an array, a List, and IEnumerable with no luck (same issue).
Does anyone have advice for submitting this object with a collection of strings using the flatten method on this type of object in Azure Table Storage?
I wanted to share how I solved this one as there isn't much clarity on using DynamicTableEntity; at least that I could find easily. If there are other suggestions I'm happy to un-mark this answer.
Option 1: Concatination
Depending on your requirements, this would be a bad option if the results of concatination exceeded the 64KB limit. I'm certain there are others, however the following method was one option that worked for me. First, I changed the model to reflect a string instead of a list of strings:
public List<string> GroupIDs { get; set; } //instead of this
public string GroupIDs { get; set; } //to this
I converted my list as follows:
var groupList = new List<string>{ "123", "456", "789" };
var userObject = new UserModel(
{
FirstName = "Jason";
GroupIDs = string.Join(",", groupList);
});
Now the userObject.GroupIDs property is a string and can be written as I've shown in the original post.
Option2: Table Design Modification
I ended up going with this second route as it was easier to store and re-hydrate the object by referencing an ID on the original object. I had to modify the UserModel to remove any reference to the groups, then I needed to add a unique ID to it as follows:
public class UserModel
{
public string FirstName { get; set; }
public string UserId { get; set; }
}
I created a table just for the groups to be referenced by the original table. I started by creating a GroupModel object to hold the UserId I needed to reference:
public class GroupModel
{
public string UserId { get; set; }
public string GroupId { get; set; }
public string GroupName { get; set; }
}
I then wrote a new method to handle each object separately as follows:
public Interface ITableOps
{
Task WriteNewUser(UserModel user, List<GroupModel> groups);
}
public class TableOps : ITableOps
{
public async Task WriteNewUser(UserModel user, List<GroupModel>, groups)
{
//...assume user table connection is created
var entity = new DynamicTableEntity(partitionKey, rowKey);
entity.Properties = TableEntity.Flatten(user, new OperationContext());
await userTable.ExecuteAsync(TableOperation.Insert(user));
//...assume group table connection is created
groups.ForEach(async g =>
{
var groupsEntity = new DynamicTableEntity(user.UserId, Guid.NewGuid().ToString());
groupsEntity.Properties = TableEntity.Flatten(g, new OperationContext());
await groupsTable.ExecuteAsync(TableOperation.Insert(groupsEntity));
});
}
Lastly, I wrote a simple method to read the entire partition of Groups from the Groups table.
Yes TableEntity.Flatten in the SDK does not support ICollection/IEnumerable type properties. But v2.0 of ObjectFlattener API here supports IEnumerable/ICollection etc. propoerties. https://www.nuget.org/packages/ObjectFlattenerRecomposer/
I put some usage example in the Nuget package description, it is very similar to using the TableEntity.Flatten/ConvertBack api but with all property types being supported.
I use EntityFrameworkCore.SQLite v1.1.3, VS 2015, make simple WPF application, .Net 4.6
People and Orders tables are related as "many-to-many" through OrdersToPeople table in my database. I've made dbContext classes using SQLite Tools.
I use this code to check loaded data:
var list = myDbContext.People
.Include(t => t.OrdersToPeople);
foreach (var element in list)
{
var c = element.OrdersToPeople.Count;
//c is always 0. Why?
}
When i load OrdersToPeople or Orders tables the same way
var list = myDbContext.OrdersToPeople
or
var list = myDbContext.Orders
, i see the data. When i make SQL-query, it returns me correct data.
Why Include does not load OrdersToPeople?
P.s. The OrdersToPeople field is virtual.
public partial class People
{
//...fields...
public virtual ICollection<OrdersToPeople> OrdersToPeople { get; set; }
public People()
{
OrdersToPeople = new HashSet<OrdersToPeople>();
}
}
public partial class OrdersToPeople
{
public long Id{ get; set; }
public long PeopleId { get; set; }
public long OrderId { get; set; }
public virtual People People { get; set; }
public virtual Orders Orders { get; set; }
}
I think the behaviour is expected. Did you try to invoke ToList on your selection?
It looks like EF is ignoring include without ToList:
Entity Framework Core ignoring .Include(..) without .ToList(..) indirectly
var list = myDbContext.People
.Include(t => t.OrdersToPeople);
.ToList();
I have two related models and want to read via linq
using (var ctx = new TextsContext())
{
var data = from e in ctx.Text
where e.LanguageCode == lang
select e;
foreach (var d in data)
{
Debug.WriteLine(d.Language, d.Fieldname);
}
}
First model
public class Language
{
public string Code { get; set; }
public string Country { get; set; }
}
Second model
public class Text
{
public string Fieldname { get; set; }
public string LanguageCode { get; set; } // Add this foriegn key property
public string Description { get; set; }
// Navigation properties
public virtual Language Language { get; set; }
}
I am using code first(Fluent API) to build relationship between the two tables.
When I want to query with linq, I've got error message:
There is already an open DataReader associated with this Command which
must be closed first.
I assume that if you do a the Include() and ToList() it will solve it. Probably because of the lazy loading of linq while it iterates it creates another reader in order to get the other entity (Language).
using (var ctx = new TextsContext())
{
var data = (from e in ctx.Text.Include("Language")
where e.LanguageCode == lang
select e).ToList();
foreach (var d in data)
{
Debug.WriteLine(d.Language, d.Fieldname);
}
}
Probably the questin title is not self-explanationary.
I have an ASP.NET MVC5 project with Entity Framework 6. I use code first and I've implemented a TPH pattern for an entity.
There's a base Request entity (I've removed most fields, it's just an example).
public class Request
{
public int Id { get; set; }
public string Title { get; set; }
}
also there's some models with exclusive properties that extend it:
public class RequestQuestion : Request
{
public string Question { get; set; }
public string Answer { get; set; }
}
public class RequestForWork : Request
{
public string WorkName { get; set; }
}
Each of them is added to the EntityContext:
public DbSet<Request> Requests { get; set; }
public DbSet<RequestQuestion> RequestQuestions { get; set; }
public DbSet<RequestForWork> RequestForWorks { get; set; }
When I create some of the requests I add them like this:
var db = new EntityContext();
var requestQuestion = new RequestQuestion{ some initialization };
this.db.Requests.Add(requestQuestion);
this.db.SaveChanges();
And here comes the question. When I query requests of the user
var requests = this.db.Students.Find(userId).Requests.ToList();
in debug I can access the properties of the extending class for every request through the base. So, is there a way to somehow get a type of class that is extending the selected entity and to access it's properties?
Currently to build a list of all requests and fill some viewmodel with data I need to seperately select every type of request and fill a global list from these seperate selects.
You need to cast the base type to its subtype and test for null
foreach (r in requests)
{
var rq = r as RequestQuestion;
if(rq != null)
{
string rq = rq.Question
}
var rfw = r as RequestForWork;
if(rfw != null)
{
string wn = rfw.WorkName;
}
}
I just want to get some opinions on the most efficient and quickest way to populate my composite class using the EF4.0. I have a parent class which has a structure similar to the class below. It mirrors my database structure.
public class Person
{
public string FirstName { get; set; }
........
public Address WorkAddress { get; set; }
public IList<Account> Workspace { get; set; }
}
public class Address
{
public string FirstName { get; set; }
......
}
public class Account
{
public string SortCode { get; set; }
public IList<string> TransactionHistory {get; set;}
......
}
So, at this moment in time I pull back all the 'Persons' from the EF and I loop through each of them and populate the Address and Accounts for each. Lazy loading is enabled, so I have to encapsulate all my loops in the using statement or my Accounts will be empty when I try to iterator through them. So, can I disable lazy loading for this call only or should approach the population of my list of persons in another manner.
using (var entities = new PersonEntities())
{
var dbPeople = (from person in entities.Persons
select person).ToList();
foreach(var person in dbPeople)
{
foreach(var account in person.Accounts)
{
// In here I populate my 'Person' business object account and add it to my collection to return.
}
}
}
If I got you right, you're going to include the relation keeping the LazyLoading enabled. You can do this for the query using the Include method:
using (var entities = new PersonEntities())
{
var dbPeople = entities.Persons.Include("Accounts").ToList();
foreach(var person in dbPeople)
{
//Do nothing with Accounts if the relation is mapped correct
}
}
EDIT:
Also you can disable LazyLoading for the created PersonEntities instance's lifetime:
using (var entities = new PersonEntities())
{
entities.Configuration.LazyLoadingEnabled = false;
//...
}