I can only use SPs to load my entities from database. I can successfully load customer by using import function:
Customer cust = context.GetCustomerById(customerId);
Then I need to load cust.Orders collection using another SP:
IEnumerable<Order> cust_orders = context.GetOrdersByCustomerId(customerId);
I get list of related orders which have property Customer correctly set to the already loaded Customer instance. The problem arises when I try to do something like:
foreach(Order ord in cust.Orders)
{
Console.WriteLine(ord.Number); // "SELECT permission required ..." exception is thrown here
}
Is there a way to Load Orders property without triggering Select?
You have to disable LazyLoading and load Orders through appropriate SP call, like this:
DBContext.DataBase.SqlQuery<Order>("exec spGetOrders {0}", invoiceID).ToList();
Related
I am new to entity framework and I am trying to get my head around it. I am used to writing stored procedures which have all the data I need on a example by example basis.
I am under the impression that I can get all values from a particular table including the foreign key values direct using entity framework without having to write a select query which joins the data.
I have the following in my controller
public ActionResult Patient()
{
using (var context = new WaysToWellnessDB())
{
var patients = context.Patients.ToList();
return View(patients);
}
}
In my view I have the following
#foreach (var item in Model)
{
<p>
#item.FirstName #item.Surname #item.Gender.GenderDesc
</p>
}
I have two tables, Patient and Gender, GenderId is a foreign key which I am trying to get the GenderDesc from that table.
I am getting the following message
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
Can someone explain why I cannot access GenderDesc. It does work if I remove the using() around my context, but I don't really want to leave that open, is there a way to get this to work still having the using around?
Thanks in advance.
Correct, you have disposed of the context as it is within a using statement, so anything you try to access from then on will not be able to be lazy loaded. The disadvantage with lazy loading is that it will perform a query for the gender for every patient you are iterating over, which is handy, but bad! I would load the related table at query time using Include.
You'll need a new import:
using System.Data.Entity;
And then include the related table:
var patients = context.Patients.Include(p => p.Gender).ToList();
That will result in a query which will join to your "Gender" table and you should be able to output item.Gender.GenderDesc in your view.
Getting an error client side with breeze: "Cannot call method 'map' of undefined" when trying to pull over some data. The difference between this action and one that works is that this action is calling a stored procedure and returning ObjectResult<T> instead of DbSet<T>.
Might this be why I get an error? Using Chrome Developer tools, I do see that the breeze controller is returning json data.
I have created a complex model type in the edmx for mapping the rows returned from the stored procedure.
The action in the breeze controller has a return type of IEnumerable<T>.
I experienced the same error when using an EF complex type. A workaround was to create a view in my database instead of using a complex type, set the stored procedure to return a type of the new view which had a primary key and then it worked. It would seem that breeze requires entities to have a primary key defined.
Hm... not quite sure what is happening, so just guessing here, but try adding an AsQueryable() to the result returned, and changing the result type to a IQueryable.
We don't have any stored proc tests for breeze yet, but this is impetus for me to add some :)
I had the very same issue, but thank God I figured out a solution. Instead of using a stored procedure, you should use a view, as Breeze recognizes views as DbSet<T>, just like tables. Say you have a SQL server table that contains two tables Customers and Orders.
Customers (**CustomerId**, FirstName, LastName)
Orders (OrderId, #CustomerId, OrderDate, OrderTotal)
Now, say you want a query that returns orders by CustomerId. Usually, you would do that in a stored procedure, but as I said, you need to use a view instead. So the query will look like this in the view.
Select o.OrderId, c.CustomerId, o.OrderDate, o.OrderTotal
from dbo.Orders o inner join dbo.Customers c on c.CustomerId = o.CustomerId
Notice there is no filtering (where ...). So:
i. Create a [general] view that includes the filtering key(s) and name it, say, OrdersByCustomers
ii. Add the OrdersByCustomers view to the entity model in your VS project
iii. Add the entity to the Breeze controller, as such:
public IQueryable<OrdersByCustomers> OrdersByCustomerId(int id)
{
return _contextProvider.Context.OrdersByCustomers
.Where(r => r.CustomerId == id);
}
Notice the .Where(r => r.CustomerId == id) filter. We could do it in the data service file, but because we want the user to see only his personal data, we need to filter from the server so it only returns his data.
iv. Now, that the entity is set in the controller, you may invoke it in the data service file, as such:
var getOrdersByCustomerId = function(orderObservable, id)
{
var query = breeze.EntityQuery.from('OrdersByCustomerId')
.WithParameters({ CustomerId: id });
return manager.executeQuery(query)
.then(function(data) {
if (orderObservable) orderObservable(data.results);
}
.fail(function(e) {
logError('Retrieve Data Failed');
}
}
v. You probably know what to do next from here.
Hope it helps.
LoadProperty or Include retrieves all relationated rows from a principal entity. How can I filter the rows retrieved from a LoadProperty call? I would want not to do a post-processing of data recover from DB.
My case is something like this
public Expression<Func<TipoReforma, bool>> predicadoFiltroIdioma(String filtro)
{
return x => x.DetalleTipoReforma.Any(y=>filtro.Contains(y.Idioma.idioma));
}
IEnumerable<T> resultado = objectSet.Where<T>(predicadoFiltroIdioma("en");
Contexto.LoadProperty(resultado.ToList()[0], "DetalleTipoReforma");
I want only "TipoReforma" but related information should be only those which idioma is "x".
Thanks in advance,
Neither LoadProperty or Include support filtering = they always load all related entities. You must use different approach. You can try to use CreateSourceQuery. Something like:
var data = ((EntityCollection<TipoReforma>)resultado.ToList[0].DetalleTipReforma)
.CreateSourceQuery().OrderBy(predicadoFiltroIdioma).ToList();
It should also fill your navigation property in the principal entity. Also make sure that lazy loading is turned off before you execute this code.
i have a view that shows the user a form and the user should upload a file and choose all the categories associated with it.
the controller that is responsible in submitting the data should
retrieve the file info and
insert data in the file category
retrieve the related category ids and
insert them as well in the table
that is abstracted by the EF just
insert the file and the category ids.
this is my problem the controller just gets some info about the category not all of it. basically it only needs the ids for the insertion
i can't use
[HttpPost]
public ActionResult SaveFile(File file, List<Category> Checkbox, HttpPostedFileBase FileUpload)
{
//some stuff
//for example got the first category and named it to category1
file.Categories.Add(category1)
}
i asked someone and he told me you have to select the category you want to insert
is this really necessary ? i only need a category id and a file id to make the insert why would i fire another request to the database that i don't really need
i am using
EF 4
MVC 3
It is better to select category first because it will save you a lot of possible problems but it is not necessary. You can use dummy category object:
var category = new Category { Id = receivedId };
file.Categories.Add(category);
You will only create new category and you will set its PK. Now you need to handle file insertion where you must explicitly instruct ObjectContext to insert only file (because your categories exists in database):
context.Files.Attach(file); // now whole object graph is attached but marked as Unchanged
context.ObjectStateManager.ChangeObjectState(file, EntityState.Added); // mark only file entity as inserted
context.SaveChanges();
You can also take opposite direction:
context.Files.AddObject(file); // all objects in object graph are marked for insertion
foreach (var category in file.Categories)
{
// you don't want to insert categories again
context.ObjectStateManager.ChangeObjectState(category, EntityState.Unchanged);
}
context.SaveChanges();
This scenario works if you know that all categories exist in your database. If you want to insert new categories together with saving file you will need to query categories first or add some information about which category is new and which is existing.
New to Entity Framework .Using EF4. I have found articles and managed to use stored procedures to return a list of entities.
But cannot see/work out how you return a single entity.
Given that I have a stored procedure "GetCustomerById" that return a single customer How do I map it?
Using the Model Browser I right click on "Function Import" and I have added my StoredProcedure
however whatever I select does not seem to return a "Single Entity"
Am I missing the obvious?
thanks a lot for any link or suggestions
When you do Function Import you need to select the entity your SP returns from the drop down (i.e. Customer). The catch is EF does NOT directly returns Customer object as per your selection but System.Data.Objects.ObjectResult which implements IEnumerable.
To be more specific, here is the generated code for your function:
public ObjectResult<Customer> GetCustomerById(Nullable<global::System.Int32> Id)
That's because EF has no idea if your SP returns a single record or a list of them so it wraps the result inside ObjectResult. You can enumerate through this to get your Customer entity like any other IEnumerable object. For example:
Customer myCustomer = context.GetCustomerById(1).First();