ASP.NET Web API 2 CRUD Operation with Entity Framework - c#

I am creating a MS Web API 2 project. I have created my Entity Framework in a separate project and am referencing it in my API. Reading over a few tutorials, it is suggested that:
"ideally, we should not return EF entity objects from the Web API. It is recommended to return DTO (Data Transfer Object) from Web API".
Hence, I have created my model in my API:
namespace MyAPI.Models
{
[Table("Customer")]
public class CustomerViewModel
{
[Key]
public int CustomerID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
}
My question is: Do I need to create a data context class for each model in my API or is it fine to use EF context class? And if I do need to create a separate context for each model, how can I achieve this by a reference to the EF context class? Below is what I have started with:
namespace MyAPI.Models
{
public class CustomerDbContext : DbContext
{
public CustomerDbContext() : base("name=CusetomerDbContext")
{
}
public DbSet<MyEFDataAccess.Customer> CustomerViewModel { get; set; }
}
}
And my Controller is:
namespace MyAPI.Controllers
{
public class CustomersController : ApiController
{
private readonly CustomerDbContext _context = new CustomerDbContext();
// GET: api/Customer
public IQueryable<CustomerViewModel> GetCustomerViewModels()
{
return _context.CustomerViewModel;
}
}
The above correctly throws an error because it cannot convert EF customer to CustomerViewModel directly!

ideally, we should not return EF entity objects from the Web API. It
is recommended to return DTO (Data Transfer Object) from Web API.
The reason for this is to make sure you can change your DB schema without changing API, and vice versa. If you accomplish that goal, then you've adhered to that given advice.
The problem you're having is basic. Type A cannot be implicitly converted to Type B. As far as the compiler is concerned, you're trying to convert a DbContext to a FormControl, and it has no idea how to do that. You need to tell it explicitly what to do. One example, albeit not great:
public DbSet<MyEFDataAccess.Customer> Customer { get; set; }
and
public IQueryable<CustomerViewModel> GetCustomerViewModels()
{
return _context.Customer.Select(
customer => new CustomerViewModel
{
// <assign properties here>
}
);
}
That being said, returning an IQueryable<T> from your Controller is a definite no-no. You definitely want to allow for the consumer to query specific records. You could do this to enable pagination, for instance:
public async Task<List<CustomerViewModel>> GetCustomerViewModels(
int skip = 0,
int take = 100
)
{
return await _context.Customer
.Skip(skip)
.Take(take)
.Select(
customer => new CustomerViewModel
{
// <assign properties here>
}
)
.ToListAsync();
}

Related

Entity Framework: object returns with an empty list at first, but then suddenly the list is populated correctly

I have the following class:
public class User
{
public int Id { get; set; }
public List<User> Connections { get; set; }
//other properties
public User()
{
Connections = new List<User>();
}
}
Then I have a DataContext class for storage:
public class DataContext : DbContext
{
public DataContext() { }
public DataContext(DbContextOptions<DataContext> options) : base(options) { }
public virtual DbSet<User> Users { get; set; }
}
And a UserService class:
public class UserService: IUserService
{
private DataContext _context;
public UserService(DataContext context)
{
_context = context;
}
public User GetById(int id)
{
return _context.Users.Find(id);
}
...
}
Now suppose I correctly stored 2 users, and I add each other to their respective connection lists.
The problem is in the following piece of code:
var user1 = _userService.GetById(userId);
---> Here user1.Connections is an empty list (unexpected)
var results = anotherList.Select(x=>
{
---> Here user1.Connections have one object inside (the other user as expected)
});
I thought it was because the List was not populated yet since it was never accessed yet, but I also have a problem with the following endpoint in a controller:
var userId = int.Parse(User.Identity.Name);
var user1 = _userService.GetById(userId);
var connectionsInfo = user1.Connections.Select(x => new
{
Id = x.Id,
//map other properties
});
return Ok(connectionsInfo);
//this time an empty list is returned in the response, instead of a list with a single object
I read it might be regarding circular dependency, but I don't get any exception.
Also, I do not understand why in one case the list is populated after and in the other case is not populated at all.
Any idea what could be causing this?
Also I do not understand why in one case the list is populated after and in the other case is not populated at all.
It's the Lazy Loading feature in the entity framework. Lazy loading means delaying the loading of related data until you specifically request for it. For more explanation and a deep dive, you can review this good article.
Entity Framework supports three ways to load related data - eager loading, lazy loading, and explicit loading. for your scenario, It would prefer to use eager loading way. for achieving this goal EF has the Include() method. so, you can update your GetById method as below:
public User GetById(int id)
{
return _context.Users
.Include(item => item.Connections)
.Find(id);
}
With the above query when you find a specific user, its connections loads at the same time too. good luck.

Unable to map List<> navigational properties using OData, EF Core and AutoMapper

I'm currently writing an ASP .NET Core API utilizing OData for querying, and Entity Framework to talk to the database.
I want to separate the domain objects from the DTOs sent to the user, so have also started to use AutoMapper to translate entity framework query results to DTOs I have created.
At this point (while I'm testing), my DTOs and domain objects are the same - just public getter/setter properties. Examples of the DTOs are as follows:
public class NoteDTO
{
public int Id { get; set; }
public string Body { get; set; }
public string Conclusion { get; set; }
public string Title { get; set; }
public ManagerDTO Manager { get; set; }
}
public class ManagerDTO
{
public int Id { get; set; }
public virtual List<ProductDto> Products { get; set; }
}
public class ProductDto
{
public int Id { get; set; }
}
I also have a test method in my NotesController for fetching notes (again, using OData) which is as follows:
[HttpGet]
[EnableQuery]
public IQueryable<NoteDTO> GetMeeting()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Note, NoteDTO>();
cfg.CreateMap<Product, ProductDto>();
cfg.CreateMap<Manager, ManagerDTO>()
.ForMember(md => md.Products, conf => conf.MapFrom(m => m.Products));
});
return _context.Notes.ProjectTo<NoteDTO>(config);
}
I then try and hit my API with the following query:
https://localhost:5001/api/Notes?$select=Id,Body,Conclusion&$top=5&$expand=Manager($select=Id)
However, this fails, and in amongst the stack trace, I'm given the following error message:
System.ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable`1[System.Tuple`3[TestEntityFramework.DataObjects.ProductDto,Microsoft.EntityFrameworkCore.Query.Internal.MaterializedAnonymousObject,Microsoft.EntityFrameworkCore.Query.Internal.MaterializedAnonymousObject]]' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable`1[TestEntityFramework.DataObjects.ProductDto]' of method 'System.Collections.Generic.IEnumerable`1[TestEntityFramework.DataObjects.ProductDto] _ToEnumerable[ProductDto](System.Collections.Generic.IEnumerable`1[TestEntityFramework.DataObjects.ProductDto])'
If I remove the List from the ManagerDTO object and the relevant Product mapping config, the query above works successfully.
I saw this comment on a GitHub issue for what sounds like the same problem, but trying to implement the suggestion hasn't helped (assuming I've understood them correctly): https://github.com/AutoMapper/AutoMapper/issues/2853#issuecomment-482317381
Has anyone else run into this problem? I'm still getting used to AutoMapper so may have missed something obvious, but from searching around this seems to be a fairly uncommon issue and so pointers as to what's going on here have been hard to come by.
I'm open to any other suggestions as to what the best way of translating an OData query to entity framework, then back to a DTO is as well - if what I'm doing here isn't optimal!
Are you using the Automapper Collection Extensions? If not, this should solve your problem: https://github.com/AutoMapper/AutoMapper.Collection

How to solve "there is already an open datareader associated with this connection"

The main problem is that when the web app is launched to the internet, when the load is high an exception is raised telling that there is already an opened data reader.
The following are the specs we use:
Entityframework 5.0.0
MySQL database
Is there a way of solving this problem without the using(){} block? Main problem of this approach is that when closed the using block I can't expand foreign key relations of entityframework objects inside the html view.
I also attach some source code, showing how we keep a single database context through the whole application
public abstract class AbstractService
{
public Entities db_model
{
get
{
return DbContext.Instance.db_model;
}
}
}
public class DbContext
{
public Entities db_model = new Entities();
private static DbContext _dbContext;
public static DbContext Instance
{
get
{
if(_dbContext == null)
{
_dbContext = new DbContext();
}
return _dbContext;
}
}
}
This answer is specifically related to the issue mentioned in the question about using the loaded entities in an ASP.NET View. The question asks about a way of solving this problem without a using block or disposing of the DbContext, however I am suggesting doing exactly this.
The reason being that it's generally desirable not to use Entity Framework objects in the ASP.NET Views because those objects are a lot more more than just plain POCO objects; they hide logic which allows them to act as a proxy to the underlying database, so they have a hidden dependency on the state of the DbContext which created them.
Here's a contrived example using EF models for Employee and Department with a DbContext:
public class CompanyDbContext : DbContext
{
public DbSet<Department> Departments { get; set; }
public DbSet<Employee> Employees { get; set; }
}
public class Department
{
public long Id { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public long Id { get; set; }
public long DepartmentId { get; set; }
public virtual Department Department { get; set; }
}
If these were used in an ASP.NET application, I would create some separate models which aren't tied to Entity Framework, to be used by ASP.NET. For example:
public class DepartmentModel
{
public long Id { get; set; }
public List<EmployeeModel> Employees { get; set; }
}
public class EmployeeModel
{
public long Id { get; set; }
public long DepartmentId { get; set; }
}
A few considerations:
According to the MSDN docs, "A DbContext represents a combination of the UnitOfWork and Repository patterns" - https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.dbcontext?redirectedfrom=MSDN&view=entity-framework-6.2.0 - Therefore the DbContext should be short lived as far as possible.
When loading data from the context, related entities can be retrieved using DbSet<>.Include() - https://learn.microsoft.com/en-us/ef/ef6/querying/related-data
Generally speaking, it makes sense to de-couple the 'data' layer from the 'view' layer - for all kinds of reasons, some of which are listed here: https://learn.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5 -- this involves mapping between the EF objects and the POCO Models.
The logic which is used to query the DbContext would query the data using EF, and return that data using POCO models so that only logic which deals directly with DbContext has any involvement with the EF objects. For example:
public List<DepartmentModel> GetAllDepartments()
{
using (var ctx = new CompanyDbContext())
{
// Ensure that related data is loaded
var departments = ctx.Departments
.Include(d => d.Employees);
// Manual mapping by converting into a new set of models to be used by the Views
var models = departments
.Select(d => new DepartmentModel
{
Id = d.Id,
Employees = d.Employees
.Select(e => new EmployeeModel
{
Id = e.Id,
DepartmentId = e.DepartmentId
})
.ToList(),
})
.ToList();
return models;
}
}
Being able to use those POCO models, while requiring some extra boilerplate code, provides complete separation between the DbContext and ASP.NET, allowing the data to be used without ASP.NET Views/Controllers being concerned by the lifetime or state of the DbContext.
Sometimes this may look as if this approach violates the 'DRY' principle, however I would point out that EF objects and ViewModel objects exist to solve different problems, and it's not uncommon for the ViewModel objects to take a different shape, or even to require additional fields/attributes which wouldn't be suitable to add to the EF classes.
Lastly, the above uses 'manual' mapping, but if the mappings are really simple and straightforward, then it could make more sense to use AutoMapper instead: Cleanest Way To Map Entity To DTO With Linq Select?

Differential save on child entities with Entity Framework 7.0.0-rc1 / EntityFrameworkCore 1.0.0

I have an application based on ASP.Net MVC 6 / ASP.Net Core 1.0, which exposes CRUD methods through a Web API.
I am using Entity Framework 7.0.0-rc1, I plan to migrate everything to EF Core 1.0 / ASP.Net Core 1.0 but haven't done this yet.
Here's a snippet of the type of model which causes me an design issue :
public class ParentEntity
{
public int Id { get; set; }
public virtual ICollection<ChildEntity> ChildCollection { get; set; } = new ChildCollection();
}
public class Child
{
public int Id { get; set; }
public virtual ParentEntity ParentEntity { get; set; }
public int? ParentEntityId { get; set; }
}
All relationships are properly ensured in my ApplicationContext class, and all CRUD operations work as expected, including Adds and Updates of the Child entities when a ParentEntity is saved.
For example, here's the kind of Save method I expose through the API :
// On some web API Save method :
[HttpPost]
public JsonResult Save(ParentEntity entity)
{
if (entity.Id > 0) _context.Update(entity);
else _context.Add(entity);
var result = _context.SaveChanges();
return Json(result);
}
The entity passed to the save method is coming from my AngularJS backend. As long as child entities have been added or modified, everything works as expected.
Let's now imagine the back end has received a ParentEntity loaded with two ChildEntity named A and B.
Now on the backend I remove the B child entity and post it to the save method.
The backend saves the changes of the data it receives : all updates are persisted on ParentEntity and on ChildEntity A.
But the B ChildEntity remains in the datastore, which seem logical since there was no indication on what to do with it.
So my question is, since it seems to me it is a very common case : how do I deal with that ?
I have imagined storing somewhere the child collection, save the changes, reload the root entity, compare the collection, determine which should be removed, then remove it, but it seems pretty hard and dirty work to me. Is there a better way ?
The answer to your question is here
https://msdn.microsoft.com/da-dk/magazine/mt694083
According to some tips explained in the MSDN blog post above, I have implemented a Client state field on every entity, through it's base class :
public enum ObjectState
{
Unchanged = 0,
Added = 1,
Modified = 2,
Deleted = 3
}
public interface IObjectWithState
{
ObjectState ClientState { get; set; }
}
public abstract class BaseEntity : IBaseEntity, IPrimaryKey
{
public int Id { get; set; }
public string Name { get; set; }
[NotMapped]
public bool IsNew => Id <= 0;
[NotMapped]
public ObjectState ClientState { get; set; } = ObjectState.Unchanged;
}
Whenever a child entity is removed from a collection on the client side, instead of being physically removed and never posted back, I will keep it on the collection, and mark it as deleted.
Then I just have to deal with that on server side :
_context.ChildEntityCollection.RemoveRange(
entity.ChildCollection.Where(c => c.ClientState == ObjectState.Deleted).ToList()
);
_context.SaveChanges();
I implemented the whole enum on every entity, but currently I only use the Deleted value, and only through a relation. Up till now, no need for other values, or any value on root entities.

How to Validate Business Rule in Service or Repo

I am hoping to create a base Entity Class that includes a validation rule that checks if a field called "Title" is unique (which of course requires a db scan). I want the inherited models to run the validation rule in the repo (or service) layer and send a ValidationResult to the (MVC) client-layer.
The problem is one of inheritance.
public interface IUniqueTitle
{
int Id { get; set; }
string Title { get; set; }
// This is a "multi-client, one database" solution.
// Data is isolated using SiteId
int SiteId { get; set; }
}
// Models such as "MemberClub" and "Assessment" will inherit from this
public class EntityUniqueTitle : IUniqueTitle
{
public int Id { get; set; }
public int SiteId { get; set; }
public string Title { get; set; }
}
// This class will be used in production
public class MemberClub : EntityUniqueTitle
{
}
I wrote an extension method that to check to see if the Title field is unique based on the SiteId
public static bool IsUniqueTitle<T>(this IQueryable<T> items, T currentEntity) where T : IUniqueTitle
{
return items.Where(
item => item.Id != currentEntity.Id // INCASE UPDATING OBJECT
& item.SiteId == currentEntity.SiteId
& item.Title == currentEntity.Title)
.Count() == 0;
}
Here is where I get stuck. Where should I put the validation?
I can put in the Repo but can't figure out how to fire the ValidationResult upon Save
public class RepoUniqueTitle<T> : IRepoUniqueTitle<T> where T : EntityUniqueTitle, new()
{
protected readonly DbContext c;
public Repo(IDbContextFactory f) { c = f.GetContext(); }
public void Insert(T o)
{
if (!c.Set<T>().IsUniqueTitle(o))
{
// ***********************
// PROBLEM HERE, HOW DO I STOP AND SEND A VALIDATIONRESULT TO THE CLIENT?
// IF POSSIBLE, AUOTMATIC WHEN MODEL.ISVALID IS CALLED
// code from base repo class for reference
// if (o is IUniqueTitleForSite)
// IoC.Resolve<IRepoUniqueTitle<T>>().Validate(o);
}
else
c.Set<T>().Add(o);
}
}
I am hoping there is a validation solution that:
Models can inherit from a base Entity
Can make db calls to the inherited entity's collection
Works with ValidationResult so it can be cleanly integrated into MVC Tier
Called during Model.isValid if possible
Note: I am using ProDinner as a basis for an "n-tier code-first EF mvc/wf" solution.
Sorry, a lot of this is new to me. Any help you can provide would be greatly appreciated!
If you have separate business logic layer you should place the validation to that layer. Otherwise why to have that layer if you don't use it to execute business rules?
Anyway unique check is tricky because there is a delay between your query and actual saving of data and another thread can insert the item with the same title during that delay. You should place unique index on Title and SiteId to enforce uniqueness in the database. In such case former problem will result in the exception which you must handle somehow but it is probably better then data duplicity.

Categories

Resources