Hello,
I have problem with EF Core feature - It automatically binds related entities together when the entities are somewhere independently attached to current dbCotnext.
Let's assume following two entities:
public class Seller {
public Guid Id {get;set;}
public List<Product> Products {get;set;}
}
public class Product {
public Guid Id {get;set;}
public Guid SellerId {get;set;}
public Seller Seller {get;set;}
}
And some code in the controller (just for imagination):
var seller = DbContext.Sellers.FirstOrDefault(e => e.Id == someId);
var products = DbContext.Products.All(t => t.SellerId == someId);
return StatusCode(200, products);
The returned JSON will be like
[
{
"id": "1234",
"sellerId": "5678",
"seller": {
"id" : "5678",
"products": ["(muted reference loop exception from json converter here.)"]
}
}
]
But I don't want the Seller to be included in each Product. If I did, I'd call Products.Include(...) for that or something else.
I don't want to crawl through entities and null the navigation properties.
I don't want to hide it with [JsonIgnore] because sometimes the relation must be included.
I also don't want to manually detach every entity all the time when this happens.
The question is, is there any way to disable or work around this behaviour?
Thanks
No, you can't/shouldn't. You need separate dto class(es).
Newtonsoft.Json is responsible for object serialization, it decides which properties must [not] be serialized. You can control it's behavior only using it's attributes. You can't control it from EF :)
And as soon as you wish sometimes to include property and sometimes not - you need two different classes (each with correct attributes). Everything other is a hack. DTO, Automapper and all this stuff - you are welcome.
BTW, having different class(es) for external API and internal data storage allows you to easily change one without breaking other (in future).
Have you tried this configuration on Startup class:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc().AddJsonOptions(a => a.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
// other code
}
You have to change model class and using nullable type.
public class Product {
public Guid Id {get;set;}
public Guid SellerId {get;set;}
public Seller? Seller {get;set;}
}
Related
I would appreciate a little help here...
Lets say that in an application we have a Data Layer and a Business Logic Layer. In the DAL we have the following entity:
public class Customer {
public string Name {get; set;}
public ICollection<Address> Addresses {get; set;}
}
public class Address {
public string Street {get; set;}
}
In the BLL we have the following POCOs:
public class CustomerDto {
public string Name {get; set;}
public ICollection<AddressDto> Addresses {get; set;}
}
public class AddressDto {
public string Street {get; set;}
}
The entities in the DAL are populated with a ligth-weight ORM and retrieve from the BLL using a repository. Ex:
public class CustomerInformationService {
private readonly ICustomerRepository _repository {get; set;}
public CustomerInformationService (ICustomerRepository repository)
{
_repository = repository;
}
public class CustomerDto Get(int id)
{
var customerEntity = _repository.Get(id);
var customerDto = /* SOME TRANSFORMATION HERE */
return customerDTO;
}
}
My questions is about the /* SOME TRANSFORMATION HERE */ part. There is a discussion in our team about how to do the "mapping".
One approach is to use a mapper either an automapper or a manual mapping.
The second approach is to use sort of like a wrapper around Entity and reference the DTO in order to save a copying operation between object. Something like this:
public class CustomerDto
{
private IEntity _customerEntity;
public IEntity CustomerEntity { get {return _customerEntity;}}
public CustomerDto(IEntity customerEntity)
{
_customerEntity = customerEntity;
}
public string Name
{
get { return _customerEntity.Name; }
}
public ICollection<Address> Addresses
{
get { return _customerEntity.Addresses; }
}
}
The second approach feels a little weird to me because _customerEntity.Addresses feels like a leak (_customerEntity's reference) between my DAL and my BLL but I am not sure.
Are there any advantages/disavantages of using one approach over the other one?
Additional info: We usually pull a max. of 1000 records at a time that would need to be transform between Entity and DTO.
You did not mentioned your "ligth-weight ORM". I will answer in two sections.
If you are using ORM that creates proxies
You should avoid exposing Entities outside certain boundary. ORMs like NHibernate/EF implement lazy loading based on proxies. If you expose Entities to application/UI layer, you will have little control over ORM behavior. This may lead to many unexpected issues and debugging will also very difficult.
Wrapping Entities in DTOs will gain nothing. You are accessing Entities anyway.
Using DTOs and mapping them with some mapper tool like AutoMapper is good solution here.
If you are using ORM that does not create proxies
Do NOT use DTOs, directly use your Entities. DTOs are useful and recommended here in many cases. But the example you given in question does not need DTOs at all.
In case you choose to use DTOs, wrapping Entities in DTOs does not make sense. If you want to use Entity anyway, why wrap it? Again, tool like AutoMapper could help.
Refer this question. It's bit different; I am asking Yes/No and you are asking How. But still it will help you.
I bet for the service layer approach. Basically because something that looks like a business object or domain object has nothing to do with DTOs.
And, indeed, you and your team should use AutoMapper instead of repeating the same code tons of times which will consist in setting some properties from A to B, A to C, C to B...
I am trying to set up EF to work on WCF and keeping the domain class models EF Agnostic.
The code is organized into 3 projects. (I am taking a stab a DDD - I am very new to it but am looking forward t learning more)
Project: QA - Domain Layer. Contains the DataContract models/entities.
References
QA.Data
Project: QA.Data - Data Layer. Contains the context and EDMX (code generation stragtegy = "none")
References
Entity Framework/System.Data.Entity
Project: QA.Repository - Data Access/Repository. Contains the repository classes
References
QA [Domain Layer]
QA.Data [Data Layer]
Entity Frame/System.DataEntity
My understanding is that the domain layer can reference the data layer but the data layer should never reference the domain. The problem that this presents is that my Domain Models/Classes are defined in the Domain layer but the Context which creates and returns them is in the Data layer. In order for my context to know to return a "Widget" object it would need a reference to the Domain layer which defined the "Widget"
My (failed) solution : My solution was to create interfaces for each Domain Model and place them in the data layer. The context would return ... IdbSet ... These interfaces would, in turn, be implemented by the Domain Models, therefore keeping my data layer from directly needing to reference my domain (which causes illegal circular references anyway). The domain models were originally contructed using "ADO.NET DbContext Generator w/WCF Support" T4 templates. This process resulted in the inclusion of the [KnownType(typeof(IWidgetPiece))] at the beginning of of the widget class defin ition. (A Widget has a navigation property ... ICollection ...)
The problem appears when I attempt to access the service, I get the following error
'QA.Data.IWidgetPiece' cannot be added to list of known types since
another type 'System.Object' with the same data contract name
'http://www.w3.org/2001/XMLSchema:anyType' is already present. If
there are different collections of a particular type - for example,
List and Test[], they cannot both be added as known types.
Consider specifying only one of these types for addition to the known
types list.
I can change these to the concrete implementations ... [KnownType(typeof(WidgetPiece))] ... but I continue to get this error because the navigation property they are referring to is still returning an IWidgetPiece interface type which it MUST do in order to satify the interface implementation.
I am trying to figure out how to keep things appropriately divided and still have the context returning what it should. the context returning Interfaces still doesn't "sit" right with me for this and other reasons but I cannot think of another way to do this, and even this is presenting the aforementioned issue. HELP!
Some code to hopefully clarify my previous ramblings ...
namespace QA.Data
{
public interface IWidgetPiece
{
String ID { get; set; }
}
public interface IWidget
{
String ID { get; set; }
ICollection<IWidgetPiece> Pieces;
}
public partial class WidgetEntities : DbContext
{
IDbSet<IWidget> Widgets { get; set; }
IDbSet<IWidgetPiece> WidgetPieces { get; set; }
}
}
namespace QA
{
[KnownType(typeof(IWidgetPiece))]
// [KnownType(typeof(WidgetPiece))]
[DataContract(IsReference = true)]
public partial class Widget : QA.Data.IWidget
{
[DataMember]
public String ID { get; set; }
[DataMember]
public virtual ICollection<IWidgetPiece> Pieces { get; set; }
}
[DataContract(IsReference = true)]
public partial class WidgetPiece : QA.Data.IWidgetPiece
{
[DataMember]
public string ID { get; set; }
}
}
namespace QA.Repository
{
public class WidgetRepository
{
public List<Widget> GetWidgetbyID(String sId)
{
WidgetEntities context = new WidgetEntities();
List<IWidget> objs = context.Widgets.Where(b => b.ID == "78").ToList();
List<Widget> widgetList = new List<Widget>();
foreach (var iwidget in widgetList)
widgetList((Widget)iwidget);
return widgetList;
}
}
}
Do you really want / need two separate models i.e. your data access layer model (edmx) and your "real" domain model? The whole point of an ORM framework like EF is so you can map your domain model to your database tables, using mappings between the physical (database) conceptual model.
Since EF4.1, you can construct your domain model and then in your data access layer map that to your database directly using a fluent API. You can also elect to reverse-engineer your POCO domain model from a database if you want to quickly get up an running.
It just seems a bit of unnecessary complexity to create an entire EF class model, only to then have to map it again into another class model (which will most likely be fairly close to the EF-generated one).
I must be doing something really wrong as this seems like a very simple extension that causes an error when you try to compile the code.
So...we have a Customer Table and in that table we have a Customer_ID. We only store the Customer ID and the rest of the data comes from a Customer Truth Center.
This Customer table is referenced and creates a Entity Customer object when we generate our entity EDMX file.
We take this Customer ID and fetch the rest of the Customer info from a WCF service thats our Customer truth center. This returns the Name, Age and such.
So...we want to extend the existing Customer entity with these additional properties however we done "persist" these in our Customer database.
Hence we created a Partial Class to extend our Entity Customer like this:
namespace UsingRIAServices.Web.Models
{
public partial class Customer
{
public string Name { get; set;}
public int Age { get; set;}
}
}
This didnt work and when you build you get the following error.
Entity "UsingRIAServices.Web.Models.Customer' has a property 'CustomerReference' with an supported type.
So...if you go into the Customer.Designer.cs you see this propery
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[BrowsableAttribute(false)]
[DataMemberAttribute()]
public EntityReference<Customer> CustomerReference
{
blah, blah
}
And note the type Customer in the EntityReference which is now an extended class with our partial.
So...I added [Datamember] to each item in our partial class...same error. I tried to [Exclude] it and get the same error.
Why is something that seems so simple and direct so difficult. Please help us figure out how to extend an entity partial class. Can you do this with data that is not in the table?
Thanks
The trick is to add your class to the Models\Shared folder of your web project and name your class Customer.shared.cs.
You then get rid of all of the using statements from your new class and add the "partial" keyword to the new class. For example:
namespace XXXX.Web
{
public partial class Customer
{
public string FullName
{
get
{
return FirstName + " " + LastName;
}
}
}
}
Is there something analogous on NHibernate regarding Entity Framework's navigation property? For example, instead of:
s.Save(new Product { Category = s.Get<Category>("FD"), Name = "Pizza" });
I wish I could write:
s.Save(new Product { CategoryId = "FD", Name = "Pizza" });
Can I inform NHibernate not to use the Product's Category property as a mechanism to save the Product's category? I want to use CategoryId instead(Read: I don't want to use DTO). Entity Framework seems able to facilitate avoiding DTO patterns altogether, while at the same time offering the full benefit of ORM(can avoid joins using navigation properties). I want the EF's offering the best of both worlds(lean mechanism for saving objects, i.e. no need to retrieve the property's object) and navigation mechanism for querying stuff
Sample from EF: http://blogs.msdn.com/b/adonet/archive/2011/03/15/ef-4-1-code-first-walkthrough.aspx
public class Category
{
public virtual string CategoryId { get; set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
}
public class Product
{
public virtual int ProductId { get; set; }
public virtual string Name { get; set; }
public virtual string CategoryId { get; set; }
public virtual Category Category { get; set; }
}
[UPDATE]
Regarding James answer, I tried seeing the NHibernate's actions in SQL Server Profiler.
// this act didn't hit the Category table from the database
var c = s.Load<Category>("FD");
// neither this hit the Category table from the database
var px = new Product { Category = c, Name = "Pizza" };
// this too, neither hit the Category table from the database
s.Save(px);
Only when you actually access the Category object that NHibernate will hit the database
Console.WriteLine("{0} {1}", c.CategoryId, c.Name);
If I understand your question, you want to save a Product with a Category without hitting the database to load the Category object. NHibernate absolutely supports this and you almost have the right code. Here is how you do it in NHibernate:
s.Save(new Product { Category = s.Load<Category>("FD"), Name = "Pizza" });
This will not hit the database to fetch the actual Category, but it will simply save a Product with the correct Category.Id. Note that you don't need (and I would recommend getting rid of Product.CategoryId).
Now why does this work with session.Load(), but not session.Get()... With session.Get(), NHibernate has to return the object or null. In .NET, there is no way for an object to replace itself with null after the fact. So NHibernate is forced to go to the database (or L1 cache) to verify that the "FD" Category actually exists. If it exists, it returns an object. If not, it must return null.
Let's look at session.Load(). If the object is not present in the database, it throws an exception. So NHibernate can return a proxy object from session.Load() and delay actually hitting the database. When you actually access the object, NHibernate will check the database and can throw an exception at that point if the object doesn't exist. In this case, we're saving a Product to the database. All NHibernate needs is the Category's PK, which it has in the proxy. So it doesn't have to query the database for the Category object. NHibernate never actually needs to hydrate an actual Category object to satisfy the save request.
This is a sample from the Fluent NHibernate website:
Compared to the Entitiy Framework I have ADD methods in my POCO in this code sample using NHibernate. With the EF I did context.Add or context.AddObject etc... the context had the methods to put one entity into the others entity collection!
Do I really have to implement Add/Delete/Update methods (I do not mean the real database CRUD operations!) in a NHibernate entity ?
public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; set; }
public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}
public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add(this);
Products.Add(product);
}
public virtual void AddEmployee(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
}
You don't have to do this for nhibernate, you have to do this for keep in-memory consistence and not repeat yourself.
Consistence in memory
If you have a two way relationship, lets say Order has Lines, and Line as a relationship to order. You don't want to have a reference from one side and not from the other.
If you just do:
order.Lines.Add(line);
You have made a reference from Order to Line, but Line.Order property remains null. So your in-memory instances are not consistent.
Don't Repeat Yourself
You can use the following code :
order.Lines.Add(line);
line.Order = order;
but you will be repeating yourself, so it is better to put this code in only one place, and the best place is as order.AddLine(..).
You don't have to. You could just call SomeStore.Products.Add(someProduct) directly from outside of your entity. But it's often good practice to make the collections 'read-only' from a public perspective, and using an add method in the entity for adding items.
One benefit of this is that you can put additional logic in there. For instance in your store example, you could set a 'storesStockedIn' collection (if there was such a thing) in the same method, and so keep all the logic about to creating that relationship in one place.
This isn't really a NHibernate thing, but rather an OOP thing. (Although I'm not familiar with EF - maybe it automates some of this for you). The design decisions are exactly the same as if it was just an unpersisted poco (without NHibernate).