EF4 How to create query with dynamic select and where statement - entity-framework-4

I am trying to create a some kind of dynamic loader for my models where I can specify which properties I need, the main purpose of it is to create a REST API which provides dynamic information as JSON for individual models.
The API would be accessed by e.g.
/api/model?id=581D1393-3436-4146-A397-E47CF5419453&fields=foo,bar,baz
For this purpose I used the Dynamic LINQ like described in ScottGu's Blog, the problem is that I need to do queries over multiple tables with joins and load data from different tables, which I can't do in this case, as far I know.
Now I use ObjectQuery<DbDataRecord> approach with Entity SQL where I can create a query however I want, but in this case I lose compiler validation and it is harder to refactor.
My question is, is there a there a best practice scenario for this kind of problem? And is it may be simpler to achieve with some other ORM?
Greetings
Russlan A.

Actually I think you can do it, though you think you can't. I am saying this because of the laze loading.
using(var someContext = new SomeContextEntities())
{
var data = someContext.Entity;
foreach(var kvp in queries)
{
switch(kvp.Key)
{
case "q1":
var val = kvp[kvp.Key];
data = data.Where(q => q.q1 == val);
break;
case "q2":
var val2 = kvp[kvp.Key];
data = data.Where(q => q.q2 == val2);
break;
case "q3":
var val3 = kvp[kvp.Key];
data = data.Where(q => q.q1.q3 == val3);
...
}
}
return data.ToList();
}
If you look at the generated script or result set, you will get what you want.

Related

Copy data from one table to another without mapping all fields (Entity Framework)

Many similar questions but I cannot find a simple solution. I want to insert the result of below query to an archive table.
var record = (from a in db.contacts
where a.id == 1
select a).FirstOrDefault();
If the table was just few columns it wouldn't be a problem to add it like that
contacts_archive ca = new contacts_archive()
{
ca.id = record.id,
ca.name = record.name,
.....
};
db.contacts_archive.Add(ca)
But what if my table has a hundred columns? Is there a better (quicker) way?
If the property names match up exactly, you can use an auto mapper like AutoMapper or Mapster to do this for you. Otherwise, you can use reflection and loop through the properties yourself if you don't wan to go through the trouble of adding a nuget package.
contacts_archive ca = new contacts_archive();
var archiveProps = ca.GetType().GetProperties();
foreach (var prop in record.GetType().GetProperties()) {
if (!archiveProps.Any(a => a.Name == prop.Name)) {
continue;
}
prop.SetValue(ca, prop.GetValue(record));
}
This assumes your names and property types match exactly, though.
Try using ValueInjector or AutoMapper to help you map similar fields from the source object to the destination object then just insert the generated destination object into the db. Here are some examples on how to use these nugets.
ValueInector and AutoMapper

Partial Lookup Lists

I'm trying to limit the amount of data coming across when implementing Lookup Lists in Breeze.JS. The Lookup Lists sample uses queries that results in full objects but I would like to project the entities to fewer properties (e.g. primary key, foreign keys, and a descriptor property) and still have Breeze.JS recognize the entity type on the client. I know how to do the projections from the client to get partials but how would I do that with Lookup Lists (either from the client or the server Web API)?
You might satisfy your intentions with a custom JsonResultsAdapter.
You're probably wondering "What is a JsonResultsAdapter?"
That's what breeze uses to interpret the JSON arriving from the server. You can read about here and here.
Perhaps more helpful is to look at the adapter in the Web API dataservice and at the example adapter from the "Edmumds" sample.
The Edmunds sample demonstrates translating a JSON source that you don't control into breeze entities.
In this case, your JsonResultsAdapter would look at each node of JSON and say "this is a Foo, this is a Bar, and that one is a Baz". Accordingly, for each of these nodes it would return { entityType: "Foo" }, return { entityType: "Bar" }, and return { entityType: "Baz" }
Now breeze knows what to do and creates corresponding entities out of the Lookups payload.
Remember to mark these entities as partial, in the same way you would if you had made a projection query that targeted a single entity type.
Fortunately, the Lookups query returns the container object that holds the Foo, Bar, and Baz collections. So you can iterate over these and mark them partial right there in the query success callback.
Once you wrap your head around THAT ... you'll want to know how to put your custom JsonResultsAdapter to work in the Lookups query ... and ONLY in the Lookups query.
You can enlist that JsonResultsAdapter exclusively for your Lookups query with a using clause.
Here's an example:
var jsa = new breeze.JsonResultsAdapter({
name: 'myLookupsJsa',
visitNode: function() {...}
});
query = query.using(jsa);
Is this overkill? Would you be better off making three trips?
Only you will know. I would like to hear from you when you try it ... and give us your suggestions on how we might make this easier in a general way.
In the lookup lists example the controller action looks like this:
[HttpGet]
public object Lookups() // returns an object, not an IQueryable
{
var regions = _contextProvider.Context.Regions;
var territories = _contextProvider.Context.Territories;
var categories = _contextProvider.Context.Categories;
return new {regions, territories, categories};
}
You could reduce the footprint using a server-side projection like this:
[HttpGet]
public object Lookups() // returns an object, not an IQueryable
{
var regions = _contextProvider.Context.Regions
.Select(x => new { id = x.RegionId, name = x.RegionName });
var territories = _contextProvider.Context.Territories
.Select(x => new { id = x.TerritoryId, name = x.TerritoryName });
var categories = _contextProvider.Context.Categories
.Select(x => new { id = x.CategoryId, name = x.CategoryName });
return new {regions, territories, categories};
}
This approach does not answer this part of your question:
and still have Breeze.JS recognize the entity type on the client
Not sure what the solution or use case is for this piece.

Performant loading of collections of Model Data using Linq in MVC

We have controllers that read Entities with certain criteria and return a set of view models containing the data to the view. The view uses a Kendo Grid, but I don't think that makes a particular difference.
In each case, we have a Linq Query that gets the overall collection of entity rows and then a foreach loop that creates a model from each row.
Each entity has certain look ups as follows:
those with a 1:1 relationship, e.g. Assigned to (via a foreign key to a single person)
those with a 1:many relationship e.g. copy parties (to 0:many people - there are not many of these)
counts of other relationships (e.g. the number of linked orders)
any (e.g. whether any history exists)
If we do these in the model creation, the performance is not good as the queries must be run separately for each and every row.
We have also tried using includes to eager load the related entities but once you get more than two, this starts to deteriorate too.
I have seen that compiled queries and LoadProperty may be an option and I am particularly interested in the latter.
It would be great to understand best practice in these situations, so I can direct my investigations.
Thanks,
Chris.
Edit - sample code added. However, I'm looking for best practice.
public JsonResult ReadUsersEvents([DataSourceRequest]DataSourceRequest request, Guid userID)
{
var diaryEventModels = new List<DiaryEventModel>();
var events = UnitOfWork.EventRepository.All().Where(e => e.UserID == userID);
foreach (var eventItem in events)
{
var diaryModel = new DiaryEventModel(eventItem);
diaryEventModels.Add(diaryModel);
}
var result = diaryEventModels.ToDataSourceResult(request);
return Json(result, JsonRequestBehavior.AllowGet);
}
public DiaryEventModel(Event eventItem) {
// Regular property from Entity examples - no issue here as data retrived simply in original query
ID = eventItem.ID;
Start = eventItem.StartDateTime;
End = eventItem.EndDateTime;
EventDescription = eventItem.Description;
// One to one looked up Property example
eventModel.Creator = eventItem.Location.FullName;
// Calculation example based on 0 to many looked up properties, also use .Any in some cases
// This is a simplified example
eventModel.AttendeeCount = eventItem.EventAttendees.Count();
// 0 to Many looked up properties
EventAttendees = eventItem.EventAttendees.Select(e => new SelectListItem
{
Text = e.Person.FullName,
Value = e.Person.ID.ToString()
}).ToList();
}

MVC and Entity Framework Key

I am new to MVC and EF and am having a heck of a time getting an EF query to work, specifically with the EF key="". After adding some test code, the error being returned here is 'The name idAddress does not exist in the current context'. There is a primary key on the Address table named idAddress - identty int
I have read through many suggestions on the site and can't get past this.
private motion_care_360Entities db = new motion_care_360Entities();
public ActionResult GetItems(GridParams g)
{
var list = db.Addresses.Include("AddressCountry").Include("AddressState").Include("AddressType").AsQueryable();
var list1 = list.OrderBy(o => idAddress).ToList();
var l1 = list1[0].AddressState.State;
return Json(new GridModelBuilder<Address>(list, g)
{
Key = "idAddress", // needed when using Entity Framework, usually it's Id
// If you're using EF, it's needed so that the data will be ordered by it before paging it
Map = o => new
{
AddressTypeType = o.AddressType.Type,
AddressStateState = o.AddressState.State,
AddressCountryCountry = o.AddressCountry.Country,
o.City,
}
}.Build());
}
}
That would need to be list.OrderBy(o => o.idAddress).ToList();
You need to specify that it's by the idAddress of the object (i.e. o from your lambda expression) itself.
You're actually confused about a lot of things. First, the issue you claim you have has nothing to do with either MVC or EF. It has to do with whatever GridModelBuilder is, and since that's not a part of MVC, you must be using some third party control for that.
Second, I'm guessing (since I don't know what third party control you're using) that Key is used when using GroupBy, not when using OrderBy. OrderBy does not have a key, but GroupBy does. But it's hard to know, since you seem to think that third party tools are part of the framework.
Third, your OrderBy is assigned to list1, which you never use. You use list, which is unordered.
Fourth, you don't need AsQueryable as it's already a Queryable.

JSON-serialization of NHibernate entities using the builtin JsonResult in ASP.NET MVC yields circular dependency error

I'm trying to return a JSON list of stuff from my server via an ASP.NET MVC front layer:
var stuff = repo.GetStuff();
return Json(stuff);
However, instead of the expected JSON, I get an error message stating
A circular reference was detected while serializing an object of type 'System.Reflection.RuntimeModule'.
I think I've found where this happens, but to explain it I need a simple example domain model as follows:
I am (lazily?) loading a selection of documents from NHibernate, like so:
var session = getNHibernateSession();
var query = new NhQueryable<Document>(session.GetSessionImplementation());
var docs = query.ToList().AsEnumerable();
I then pass the documents to return a JsonResult in my controller:
return Json(docs, JsonRequestBehavior.AllowGet);
Now, when Json() serailizes the collection, it walks over the properties of a document, finds a person. It serializes that person, and finds a project. It serializes the project, and finds - that's right - the person again! Since I'm lazy loading, it can just keep walking for ever if nothing stops it, but it's stopped by a circular reference error.
I don't really need to go all these levels down (I'd be fine without loading the project in the first place) - can I somehow affect how Json() serializes this collection, to not go further than, say, 2 levels down? I've googled around a little, but most of what I find seems to be from people who decided to use a serializing library directly, rather than just using the built-in functionality in .NET MVC. (Note: The solution to this problem must be possible to apply specifically to this case, since I might want to get JSON lists of people, including projects, somewhere else in the application...)
If you are retrieving Json, you have a service api. You have to design the api besides the implementation. Does the page that will be using it need all those fields and collections? probably not. What about adding more properties for other features and services? They will start appear in all the requests.
What you need is to use a ViewModel or just an anonymous type with the desired structure:
var session = getNHibernateSession();
var query = new NhQueryable<Document>(session.GetSessionImplementation());
var docs = query.ToList();
var result = query.Select(x => new {
x.Id,
x.Name,
People = new { p.Id,
p.Name,
p.Title
}
});
return Json( result, JsonRequestBehavior.AllowGet);
This way you can control what is being rendered and how.
It's already been answered here.
Also, it's generally a bad idea to expose your domain entities like this. If it's for read-only purposes it might not be so bad, but if any of your action methods accept a domain entity, then a specifically formatted request can overwrite properties on your domain entity that you don't want to (such as your PK).
To preserve object references in JSON, add the following code to Application_Start method in the Global.asax file:
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;

Resources