I am working in EF 4.3.1 and everything is going pretty well on the Insert side. But now I am trying to pull the objects I inserted back out to show in a View in my MVC3 project and having problems.
In my DAL, I have the following method to get a Church object from the database:
public virtual TEntity GetById(object id)
{
return _db.Find(id);
}
The object itself looks like this:
public class Church
{
//Basic Properties
public int Id { get; set; }
public string Name { get; set; }
public string Website { get; set; }
... (some properties)
//Church Contacts and Services
public List<Contact> Contacts { get; set; }
public List<Service> Services { get; set; }
....
}
Notice the last two Properties: Contacts and Services. Each is a 1:Many table within my database. It appears that Find() does not return any of this, though. It just sets the two properties to null.
The actual entries in the database look just how I would expect them to. Each Church has a couple contacts and services associated with them. Yet, it never maps back to the model...
Any thoughts?
EDIT Yeah, looks like it wasn't loading those properties until I explicitly told it to. I modified my query by adding this to my ChurchRepository (inherits from GenericRepository)
public override Church GetById(object id)
{
return _db.Include("Contacts").Include("Services").FirstOrDefault(c => c.Id == (int)id);
}
You should mark navigation properties as virtual or you could also include them without lazy loading that is best practice.
Try this
db.Churces.Include(entity=>entity.Services).
Include(entity=>entity.Contacts).SingleOrDefault(entity=>entity.Id == id)
You may have to import System.Data.Entity namespace for Include extension method.
Related
In my ASP.NET Core we're using EF Core. When saving a record that has a list of child records we get the following error:
The instance of entity type Child cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.
Step-by-step:
1. fetch record from the controller and pass it to the view (view has a )
2. update some properties via the html form
3. click save
4. catch model passed in the controller Save method. Get the original item from DB and save changes (as made via the form)
5. call update/save in the repository
Simplified code below:
// Get the record from the database
var record = _dbContext.Parents
.Include(x => p.SomeOtherObject)
.Include(x => x.ListChildren)
.FirstOrDefault(x => x.IdParent == id);
// then we do some changes to Parent and ListChildren
// we do not do any changes to SomeOtherObject!!!
// save changes
_dbContext.Update(record);
_dbContext.SaveChanges();
// definition of entities
public class Parent
{
public int IdParent { get; set; }
public string Name {get; set;}
public string Surname {get; set;}
public int IdSomeOtherObject { get; set; }
[ForeignKey("IdSomeOtherObject")]
public virtual SomeOtherObject SomeOtherObject { get; set; }
public virtual List<Child> ListChildren { get; set; }
}
public class Child
{
public int IdChild { get; set; }
public int IdParent { get; set;}
public string Name { get; set; }
}
public class SomeOtherObject
{
public int IdSomeOtherObject { get; set; }
public string PropertiesBlahBla { get; set; }
}
Now, I know that we can add .AsNoTracking() to the Get operation, but then the problem is that when saving Parent EntityFramework will perform and UPDATE SQL statement even for the SomeOtherObject (that was not changed in any way) and that is not acceptable for our data/input scenario.
Is there any other way to get pass this error?
Try removing _dbContext.Update(record);. Your entities should already be tracked, so changes should be saved.
As Update docs state:
Begins tracking the given entity and entries reachable from the given entity using the Modified state by default
So it seems that in this scenario it is not needed.
UPD
During discussions in chat was discovered that child tracked collection was substituted like this:
record.ListChildren = someModel.ListChildren.Where(...).ToList()
Which resulted in addition of elements with already tracked ids. So the ListChildren should be updated with saving already tracked items like recommended here.
I have asp.net web api application. I have the table Companies in the databse which have two fields: id and description. Recently I've updated the database and added a new column called CustomerID. After that when I am trying to call getCompanies
private readonly BackendContext _context;
public CompaniesController(BackendContext context)
{
_context = context;
}
// GET: api/Companies
[HttpGet]
public IEnumerable<Company> GetCompanies()
{
return _context.Companies;
}
I get
I think the controller tries to return the old companies model but can't achieve it because it doesnt exist now but I don't know how to fix this though the controller should return the updated model. Maybe I should somehow rebuild the app to make it use the updated version?
Additional code:
Context
public class BackendContext : Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext<IdentityUser>//DbContext
{
public BackendContext(DbContextOptions<BackendContext> options) : base(options) { }
public DbSet<Company> Companies { get; set; }
public DbSet<CompanyToProduct> CompanyToProducts { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Vendor> Vendors { get; set; }
public DbSet<VendorToProduct> VendorToProducts { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<InvoiceItem> InvoiceItems { get; set; }
}
Model
public class Company
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<CompanyToProduct> CompaniesToProducts { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
UPDATE
I've added some values to the table and I got the response of the first company:
[{"id":1,"name":"Google","description":"free food","customerID":6,"customer":null,"companiesToProducts":null,"invoices":null}
BUT I also got the fields which is not specified in the table: customer, companiesToProducts,invoices. Invoices and companiesToProducts are tables in my database and I don't know what is customer referred to. I should also mention that these tables are connected by foreign key.
UPDATE
Error:
Based on the comments on the question above, it sounds like the related tables are all trying to serialize and the overall process is failing likely due to circular references in the object graph. This comment above in particular hints at a solution:
I want to return only the data about companies but the controller also returns another fields like customer, companiesToProducts,invoices
While it's convenient to just return directly from the data context, this has the added side-effect of coupling the API with the database (and with the data access framework, which appears to be the issue here). In API design in general it's always a good idea to explicitly define the "shape" of that API. The fields to return, etc.
Project your result into an explicitly defined shape and return only what you want to return:
var result = _context.Companies
.Select(c => new
{
c.ID,
c.Name,
c.Description,
c.CustomerID
})
.ToList();
This defines specifically what you want to return, fetches only that information from the backing data, materializes it into an in-memory list, and finally then returns it through the API.
There is a potential downside to this, however. Because now we also need to change the return type of your API method. There are a couple options there, such as returning a generic response object or creating a view model which closely approximates your already existing model and starts to feel like duplication.
As with just about anything, it's a balance. Too far in any one direction and that direction starts to become a problem. Personally I often go the route of defining a view model to return:
public class CompanyViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CustomerID { get; set; }
}
and returning that:
return _context.Companies
.Select(c => new CompanyViewModel
{
ID = c.ID,
Name = c.Name,
Description = c.Description,
CustomID = c.CustomerID
})
.ToList();
But the reason I normally do this is because I normally work in an environment where the web application is just one application attached to a common shared business domain, so the view models don't feel like code duplication. They're in a separate project, often take a different shape than the backing data objects, etc. But if your domain models are already in your web project and that's the only project you have, there's a strong desire to want to return those.
Another option when that's the case could be to universally set your JSON serialization to ignore circular references:
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore );
But do keep in mind that this still couples your API to your DB models. Maybe that's okay in this project, but if you ever add a column to your DB that you don't want users to see then it becomes an issue. As with anything, you have options.
I have two entities from my database exposed in an ASP.NET WebApi 2 OData: service Employee and Activity. For simplicity, let's assume they look like this:
public class Employee {
public int Id { get; set; }
public string Type { get; set; }
}
public class Activity {
public int Id { get; set; }
public int EmployeeId { get; set; }
public virtual Employee OpenedBy { get; set; }
}
Please note that the OpenedBy property corresponds to a navigation property, e.g. I can run the following OData query:
GET http://localhost/odata/Activities?$expand=OpenedBy
I would like to block certain Employee types from being shown in OData. Let's assume I can't do this on the data source, so I have to do it in code.
What I've done so far is to block these types in the EmployeesController (inherits from EntitySetController):
[Queryable]
public override IQueryable<Employee> Get() {
return dbContext.Employees.Where(e => e.Type != "Restricted").AsQueryable();
}
[Queryable]
protected override Employee GetEntityByKey([FromODataUri] int key) {
var employee = dbContext.Employees.Find(key);
if (employee == null || employee.Type == "Restricted") {
throw new ODataException("Forbidden");
}
return employee;
}
This works fine. However, I noticed that if I run the query:
GET http://localhost/odata/Activities?$expand=OpenedBy
I do not hit the code in the Employees controller and consequently the restricted employee records are visible. What is a good way to prevent this from happening?
In this case: http://localhost/odata/Activities?$expand=OpenedBy
I think you should change the code in ActivitiesController, $expand will hit Get Activity method there.
If you do not want to expand the OpenedBy all the time, you can add an attribute:
[NotExpandable]
Hope this can help :)
Since you say "I noticed that if I run the query" I get the impression that this is a side-effect that you are happy to restrict in all circumstances. If this is the case this article by Mike Wasson could be of use to you.
In that article he suggests two methods to restrict odata access to a property:
An attribute on your model
Programmatically removing it from your EDM
I didn't try the first and I'm not sure which namespace or libraries you would need to do it but, in the case of the question it would look like this:
public class Activity {
public int Id { get; set; }
public int EmployeeId { get; set; }
[IgnoreDataMember]
public virtual Employee OpenedBy { get; set; }
}
I have used the second method and this would look something like this for the example given in the question:
var activities = modelBuilder.EntitySet<Activity>("Activities");
activities.EntityType.Ignore(a => a.OpenedBy);
I have restricted some navigational Collections this way and it works very well.
I have the following class:
public class Delivery
{
// Primary key, and one-to-many relation with Customer
public int DeliveryID { get; set; }
public virtual int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
// Properties
string Description { get; set; }
}
Can someone explain why they Customer information is coded with virtual. What does it mean?
Judging by the comments, you are learning Entity Framework?
virtual here would mean you are trying to use lazy loading - when related items like Customer can be loaded by EF automatically
http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx
For example, when using the Princess entity class defined below, the related unicorns will be loaded the first time the Unicorns navigation property is accessed:
public class Princess
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Unicorn> Unicorns { get; set; }
}
Can someone explain why they Customer information is coded with virtual. What does it mean?
The virtual keyword means that a super class derived from this base class (i.e. Delivery) can override that method.
If the method was not marked as virtual then it would not be possible to override that method.
Guess you are using EF.
What happens when you make a NavigationProperty virtual is that EF dynamically creates a derived class.
That class implements functionality that allows for lazy loading and other tasks like maintaining relations that EF performs for you.
Just to get the idea your sample class dynamically becomes something like this:
public class DynamicEFDelivery : Delivery
{
public override Customer Customer
{
get
{
return // go to the DB and actually get the customer
}
set
{
// attach the given customer to the current entity within the current context
// afterwards set the Property value
}
}
}
You can easily see this while debugging, the actual instance types of your EF classes have very weird names, since they are generated on the fly.
Good morning everyone,
I am trying to tackle a problem I run into with EF code first. My schema is the following
public class Article : IUrlNode
{
[Key]
public Guid ArticleID { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateUpdated { get; set; }
public string Summary { get; set; }
[System.ComponentModel.DataAnnotations.InverseProperty("CategoryArticles")]
public virtual IQueryable<Category> ArticleCategories { get; set; }
public string FriendlyUrl
{
get;
set;
}
}
[RouteChild("CategoryArticles")]
public class Category : ContentNode
{
public Guid ServiceId { get; set; }
[System.ComponentModel.DataAnnotations.InverseProperty("ArticleCategories")]
public virtual IQueryable<Article> CategoryArticles { get; set; }
}
I have written code with which I am able to retrieve a category from the database without actually knowing that its a category. From then on I must retrieve a single article of that category again without knowing that its an article. For categories I am relying on the ContentNode base class and for Articles on the IUrlNode interface.
Category retrieval works fine and with a single query but after I actually get the category I have to use reflection to get the navigation property pointed by the RouteChild attribute to find that single article that matches my criteria. Problem is that the navigation property type is ICollection which means that it will at best use lazy loading and will bring all the articles from the database and will find the one I am looking for in memory.
My problem is also described in this previous post (not by me):
Entity Framework Code First IQueryable
Is there a way to have that navigation property as IQueryable or some other design that can go around this limitation?
No there is no way to have navigation property as IQueryable but you can change the collection to IQueryable by using:
IQueryable<Article> query = context.Entry(category).Collection(c => c.articles).Query();
query.Where(...).Load();
Generally your "algorithm" looks pretty strange. You want to work with base class but in the same time you want to access child properties. That sounds wrong and it can most probably be solved in better way (non "generic" way is also better).