I'm learning Entity Framework, I was a bit confused between BLL and DAL, according to my search, I found that Entity Framework is DAL.
There are two ways to create BLL and DAL below:
First approach: write a separate DAO for each object (including add, remove, findAll, ...). In the BLL will call the DAO to get the data or modify the necessary data.
I have StudentManagement which inherits from DbContext and placed in the DAL.
public partial class StudentManagement : DbContext
{
public StudentManagement()
: base("name=StudentManagement")
{
}
public virtual DbSet<LOP> LOP { get; set; }
public virtual DbSet<STUDENT> STUDENT { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<LOP>()
.HasMany(e => e.STUDENT)
.WithOptional(e => e.LOP)
.HasForeignKey(e => e.CLASS_ID);
}
}
StudentDAO: query and modifying data if necessary.
class StudentDAO
{
public StudentManagement context { get; set; }
public StudentDAO()
{
context = new StudentManagement();
}
public IQueryable<STUDENT> findAll()
{
return context.STUDENT;
}
public void add(STUDENT student)
{
context.STUDENT.Add(student);
context.SaveChanges();
}
public void remove(int id)
{
STUDENT student = context.STUDENT.Find(id);
if (student != null)
{
context.STUDENT.Remove(student);
context.SaveChanges();
}
}
}
Student_BLL: call the StudentDAO to handle business and then return data to view.
class StudentBLL
{
public List<STUDENT> getStudentInClass(int ClassID)
{
return new StudentDAO().findAll().Where(student => student.CLASS_ID == ClassID).ToList();
}
public List<STUDENT> findAll()
{
return new StudentDAO().findAll().ToList();
}
public STUDENT find(int id)
{
return new StudentDAO().findAll().FirstOrDefault(student => student.ID == id);
}
public void add(STUDENT student)
{
new StudentDAO().add(student);
}
public void remove(int id)
{
new StudentDAO().remove(id);
}
}
Another approach: I don't have to create DAO for each object but use context in BLL and query directly using LINQ.
class LopSH_BLL
{
public StudentManagement context { get; set; }
public LopSH_BLL()
{
context = new StudentManagement();
}
public List<LOP> findAll()
{
return context.LOP.ToList();
}
public LOP find(int id)
{
return context.LOP.Find(id);
}
public void add(LOP lop)
{
context.LOP.Add(lop);
context.SaveChanges();
}
public void remove(int id)
{
LOP lop = context.LOP.Find(id);
context.LOP.Remove(lop);
context.SaveChanges();
}
}
Which is better and does it follow the rules of 3 layers?
Although there is nothing wrong with the way you are accessing data, there are better approaches available as best practices. However, you should always consider the type of project before planning any specific software architecture.
Ask yourself a few questions:
Is this project going to grow over time, or it's just a simple project to apply some simple logic?
How many developers are going to work on the project?
I believe these two simple questions can guide you much more accessible to deciding the architecture of your project.
Now regarding your question:
Which is better, and does it follow the rules of 3 layers?
Nothing wrong with the way you are accessing data, but:
Program to the interfaces.
Using interfaces is a crucial factor in making your code easily testable and removing unnecessary couplings between your classes.
Check this post : What does it mean to "program to an interface"?
Dependency Inversion & Dependency Injection
Understanding the meaning of these two and knowing the differences can help you so much down the road.
Check this post: Difference between dependency injection and dependency inversion
Repository Pattern
The Repository Design Pattern in C# (or any OOP-supported language) mediates between the domain and the data mapping layers using a collection-like interface for accessing the domain objects. In other words, we can say that a repository design pattern acts as a "middle layer" between the rest of the application and the data access logic.
I believe this is an excellent example to check and learn: The Repository Pattern Example in C#
Last but not least, there are some well-proven architecture patterns in general which are good to know if you are serious in this journey:
Domain Driven Design (DDD)
Microservices Architecture Pattern
I've been having some rough time sorting out how to create an Restful API with EntityFramework. The problem is mainly because this API should be used in a long time forward and i want to be it as maintainable and clean, with good performance. Enough of that, let's get into the problem.
Disclamer:
Because of companypolicy and can't post too much here, but i will try to address the problem the best way possible. There will also just be snippets of code, and may not be valid. I'm also fairly new to C# and as a JuniorD i have never touched an API before.. And excuse my english, this is my second language.
Every model derives from the BaseModel class
public class BaseModel
{
[Required]
public Guid CompanyId { get; set; }
public DateTime CreatedDateTime { get; set; }
[StringLength(100)]
public string CreatedBy { get; set; }
public DateTime ChangedDateTime { get; set; }
[StringLength(100)]
public string ChangedBy { get; set; }
public bool IsActive { get; set; } = true;
public bool IsDeleted { get; set; }
}
public class Carrier : BaseModel
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public Guid CarrierId { get; set; }
public int CarrierNumber { get; set; }
[StringLength(100)]
public string CarrierName { get; set; }
[StringLength(100)]
public string AddressLine { get; set; }
public Guid? PostOfficeId { get; set; }
public PostOffice PostOffice { get; set; }
public Guid? CountryId { get; set; }
public Country Country { get; set; }
public List<CustomerCarrierLink> CustomerCarrierLinks { get; set; }
}
Every repository derives from Repository and has it's own interface.
public class CarrierRepository : Repository<Carrier>, ICarrierRepository
{
public CarrierRepository(CompanyMasterDataContext context, UnitOfWork unitOfWork) : base(context, unitOfWork) { }
#region Helpers
public override ObjectRequestResult<Carrier> Validate(Carrier carrier, List<string> errorMessages)
{
var errorMessages = new List<string>();
if(carrier != null)
{
var carrierIdentifier = (carrier.CarrierName ?? carrier.CarrierNumber.ToString()) ?? carrier.CarrierGLN;
if (string.IsNullOrWhiteSpace(carrier.CarrierName))
{
errorMessages.Add($"Carrier({carrierIdentifier}): Carrier name is null/empty");
}
}
else
{
errorMessages.Add("Carrier: Cannot validate null value.");
}
return CreateObjectResultFromList(errorMessages, carrier); // nonsense
}
}
UnitOfWork derives from class UnitOfWorkDiscoverySet, this class initializes the repository properties using reflection and also contains a method (OnBeforeChildEntityProcessed) for calling every OnBeforeChildEntityProcessed.
public class UnitOfWork : UnitOfWorkDiscoverySet
{
public UnitOfWork(CompanyMasterDataContext context)
: base(context){}
public CarrierRepository Carriers { get; internal set; }
public PostOfficeRepository PostOffices { get; internal set; }
public CustomerCarrierLinkRepository CustomerCarrierLinks { get; internal set; }
}
public IRepository<Entity> where Entity : BaseModel
{
ObjectRequestResult<Entity> Add(Entity entity);
ObjectRequestResult<Entity> Update(Entity entity);
ObjectRequestResult<Entity> Delete(Entity entity);
ObjectRequestResult<Entity> Validate(Entity entity);
Entity GetById(Guid id);
Guid GetEntityId(Entity entity);
}
public abstract class Repository<Entity> : IRepository<Entity> where Entity : BaseModel
{
protected CompanyMasterDataContext _context;
protected UnitOfWork _unitOfWork;
public Repository(CompanyMasterDataContext context, UnitOfWork unitOfWork)
{
_context = context;
_unitOfWork = unitOfWork;
}
public ObjectRequestResult<Entity> Add(Entity entity)
{
if (!EntityExist(GetEntityId(entity)))
{
try
{
var validationResult = Validate(entity);
if (validationResult.IsSucceeded)
{
_context.Add(entity);
_context.UpdateEntitiesByBaseModel(entity);
_context.SaveChanges();
return new ObjectRequestResult<Entity>()
{
ResultCode = ResultCode.Succceeded,
ResultObject = entity,
Message = OBJECT_ADDED
};
}
return validationResult;
}
catch (Exception exception)
{
return new ObjectRequestResult<Entity>()
{
ResultCode = ResultCode.Failed,
ResultObject = entity,
Message = OBJECT_NOT_ADDED,
ErrorMessages = new List<string>()
{
exception?.Message,
exception?.InnerException?.Message
}
};
}
}
return Update(entity);
}
public virtual ObjectRequestResult Validate(Entity entity)
{
if(entity != null)
{
if(!CompanyExist(entity.CompanyId))
{
return EntitySentNoCompanyIdNotValid(entity); // nonsense
}
}
return EntitySentWasNullBadValidation(entity); // nonsense
}
}
DbContext class:
public class CompanyMasterDataContext : DbContext {
public DbSet<PostOffice> PostOffices { get; set; }
public DbSet<Carrier> Carriers { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<CustomerCarrierLink> CustomerCarrierLinks { get; set; }
public UnitOfWork Unit { get; internal set; }
public CompanyMasterDataContext(DbContextOptions<CompanyMasterDataContext> options)
: base(options)
{
Unit = new UnitOfWork(this);
}
public void UpdateEntitiesByBaseModel(BaseModel baseModel)
{
foreach (var entry in ChangeTracker.Entries())
{
switch (entry.State)
{
case EntityState.Added:
entry.CurrentValues["CompanyId"] = baseModel.CompanyId;
entry.CurrentValues["CreatedDateTime"] = DateTime.Now;
entry.CurrentValues["CreatedBy"] = baseModel.CreatedBy;
entry.CurrentValues["IsDeleted"] = false;
entry.CurrentValues["IsActive"] = true;
Unit.OnBeforeChildEntityProcessed(entry.Entity, enumEntityProcessState.Add);
break;
case EntityState.Deleted:
entry.State = EntityState.Modified;
entry.CurrentValues["ChangedDateTime"] = DateTime.Now;
entry.CurrentValues["ChangedBy"] = baseModel.ChangedBy;
entry.CurrentValues["IsDeleted"] = true;
Unit.OnBeforeChildEntityProcessed(entry.Entity, enumEntityProcessState.Delete);
break;
case EntityState.Modified:
if (entry.Entity != null && entry.Entity.GetType() != typeof(Company))
entry.CurrentValues["CompanyId"] = baseModel.CompanyId;
entry.CurrentValues["ChangedDateTime"] = DateTime.Now;
entry.CurrentValues["ChangedBy"] = baseModel.ChangedBy;
Unit.OnBeforeChildEntityProcessed(entry.Entity, enumEntityProcessState.Update);
break;
}
}
}
}
DiscoveryClass:
public abstract class UnitOfWorkDiscoverySet
{
private Dictionary<Type, object> Repositories { get; set; }
private CompanyMasterDataContext _context;
public UnitOfWorkDiscoverySet(CompanyMasterDataContext context)
{
_context = context;
InitializeSets();
}
private void InitializeSets()
{
var discoverySetType = GetType();
var discoverySetProperties = discoverySetType.GetProperties();
Repositories = new Dictionary<Type, object>();
foreach (var child in discoverySetProperties)
{
var childType = child.PropertyType;
var repositoryType = childType.GetInterfaces()
.Where( i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRepository<>))
.FirstOrDefault();
if (repositoryType != null)
{
var repositoryModel = repositoryType.GenericTypeArguments.FirstOrDefault();
if (repositoryModel != null)
{
if (repositoryModel.IsSubclassOf(typeof(BaseModel)))
{
var repository = InitializeProperty(child); //var repository = child.GetValue(this);
if (repository != null)
{
Repositories.Add(repositoryModel, repository);
}
}
}
}
}
}
private object InitializeProperty(PropertyInfo property)
{
if(property != null)
{
var instance = Activator.CreateInstance(property.PropertyType, new object[] {
_context, this
});
if(instance != null)
{
property.SetValue(this, instance);
return instance;
}
}
return null;
}
public void OnBeforeChildEntityProcessed(object childObject, enumEntityProcessState processState)
{
if(childObject != null)
{
var repository = GetRepositoryByObject(childObject);
var parameters = new object[] { childObject, processState };
InvokeRepositoryMethod(repository, "OnBeforeEntityProcessed", parameters);
}
}
public void ValidateChildren<Entity>(Entity entity, List<string> errorMessages) where Entity : BaseModel
{
var children = BaseModelUpdater.GetChildModels(entity);
if(children != null)
{
foreach(var child in children)
{
if(child != null)
{
if (child.GetType() == typeof(IEnumerable<>))
{
var list = (IEnumerable<object>) child;
if(list != null)
{
foreach (var childInList in list)
{
ValidateChild(childInList, errorMessages);
}
}
}
ValidateChild(child, errorMessages);
}
}
}
}
public void ValidateChild(object childObject, List<string> errorMessages)
{
if(childObject != null)
{
var repository = GetRepositoryByObject(childObject);
var parameters = new object[] { childObject, errorMessages };
InvokeRepositoryMethod(repository, "Validate", parameters);
}
}
public void InvokeRepositoryMethod(object repository, string methodName, object[] parameters)
{
if (repository != null)
{
var methodToInvoke = repository.GetType().GetMethod(methodName);
var methods = repository.GetType().GetMethods().Where(x => x.Name == methodName);
if (methodToInvoke != null)
{
methodToInvoke.Invoke(repository, parameters);
}
}
}
public object GetRepositoryByObject(object objectForRepository)
{
return Repositories?[objectForRepository.GetType()];
}
public object GetObject<Entity>(Type type, Entity entity) where Entity : BaseModel
{
var childObjects = BaseModelUpdater.GetChildModels(entity);
foreach (var childObject in childObjects)
{
if (childObject.GetType().FullName == type.FullName)
{
return childObject;
}
}
return null;
}
}
}
The problem:
I want to validate data in every model and the childmodels properties/list, know you might say this can be done using attributes, but the validation can be fairly complex and i prefer to separate this in it's own space.
The way i have attacked this problem is by using reflection from the UnitDiscoverySet class, here i can find every child of the Entity i'm trying to process and search for an appropriate Repository containg the UnitOfWork. This works by all means, just need some more work and cleanup, but for some reason i feel like this is the cheating/wrong way to attack the problem, and i'm also not getting the compile-time errors + reflection comes at a cost.
I could validate children of the entity in the entity repository, but then i would be repeating myself all over the place, and this solution don't seem right either.
I don't want this solution to depend to heavy on entityframework, since it's not given that we will use this forever.
This solution also heavly depends on the UpdateEntitiesByBaseModel method inside The DbContext. So it only changes the fields that should be changed.
Not sure that i addressed this problem as good as thought i would, but i appreciate every contribution that will lead me down the right path. Thank you!
Solution(Edit):
I ended up only using the navigation properties for GET operations, and excluded it for insert operations. Made everything more flexible and faster, this way i didn't need to use the EF Tracker, which made an insert operation of 5000 entities from a 13 minute operation, down to 14.3 seconds.
This question would probably be best asked in CodeReview rather than SO which is geared towards specific code-related questions. You can ask 10 different developers and get 10 different answers. :)
Reflection definitely does have a cost, and it's not something I like to use very much at all.
I don't want this solution to depend to heavy on entityframework,
since it's not given that we will use this forever.
This is a fairly common theme I see in applications and frameworks that development teams I work with try to cope with when working with ORMs. To me, abstracting EF from the solution is like trying to abstract away parts of .Net. There is literally no point because you forfeit access to much of the flexibility and capability that Entity Framework offers. It leads to more, complex code to deal with things that EF can do natively leaving room for bugs as you re-invent the wheel, or leaving gaps that later have to be worked around. You either trust it, or you shouldn't use it.
I could validate children of the entity in the entity repository, but
then i would be repeating myself all over the place, and this solution
don't seem right either.
This is actually the pattern I've come to advocate for with projects. Many people are against the Repository pattern, but it is a great pattern to serve as a boundary for the domain for testing purposes. (No need to set up in-memory databases or attempt to mock a DbContext/DbSets) However, IMO the Generic Repository pattern is an anti-pattern. It separates entities concerns from one another, however in many cases we are dealing with entity "graphs" not individual entity types. Rather than defining repositories per entity, I opt for something that is effectively a repository per controller. (With repositories for truly common entities such as lookups for example.) There are two reasons behind this:
Fewer dependency references to pass around / mock
Better serves SRP
Avoid pigeon-holing DB operations
The biggest problem I have with generic or per-entity repositories is that while they appear to be conforming to SRP (responsible for operations for a single Entity) I feel they violate it because SRP is about having only one reason to change. If an I have an Order entity and an Order repository I may have several areas of the application that load and interact with orders. Methods to interact with Order entities now are called in several different places, and that forms many potential reasons for methods to be adjusted. You end up either with complex, conditional code, or several very similar methods to serve specific scenarios. (Orders for listing orders, orders by customer, orders by store, etc.) When it comes to validating entities, this is commonly done in context to the entire graph so it makes sense to centralize this in code related to the graph rather than individual entities. This goes for generic base operations like Add/Update/Delete. 80% of the time this works and saves effort, but that remaining 20% either has to get shoe-horned into the pattern, leading to inefficient and/or error-prone code, or work-arounds. K.I.S.S. should always trump D.N.R.Y when it comes to software design. Consolidation into base classes and the like is an optimization that should be done as the code evolves when "identical" functionality is identified. When it's done up-front as an architecture decision, I consider this premature optimization which creates obstacles for development, performance issues, and bugs when "similar" but not "identical" behaviour is grouped together leading to conditional code creeping in for edge cases.
So instead of an OrderRepository to serve orders, if I have something like a ManageOrderController, I will have a ManageOrderRepository to serve it.
For instance I like to use DDD styled methods for managing entities where my repositories play a part in the construction since they are privy to the data domain and can validate/retrieve related entities. So a typical repository would have:
IQueryable<TEntity> GetTEntities()
IQueryable<TEntity> GetTEntityById(id)
IQueryable<TRelatedEntity> GetTRelatedEntities()
TEntity CreateTEntity({all required properties/references})
void DeleteTEntity(entity)
TChildEntity CreateTChildEntity(TEntity, {all required properties/references})
Retrieval methods, including a "By ID" as it's a common scenario, return IQueryable so that callers can control how the data is consumed. This negates the need to try and abstract the Linq capabilities that EF can leverage so callers can apply filters, perform paging, sorting, then consume the data how they need. (Select, Any, etc.) The repository enforces core rules such as IsActive, and tenancy/authorization checks. This serves as a boundary for tests since mocks just have to return List<TEntity>.AsQueryable() or wrapped with a async-friendly collection type. (Unit-testing .ToListAsync() using an in-memory) The repository also serves as a go-to place for retrieving any related entities via whatever criteria is applicable. This can be viewed as potential duplication but changes to this repository will only be needed when the controller/view/area of the application needs to change. Common stuff like lookups would be pulled via their own repository. This cuts down on the need for lots of individual repository dependencies. Each area takes care of itself so changes/optimizations here don't need to consider or impact other areas of the application.
"Create" methods manage the rules around creating and associating entities to the Context to ensure that entities are always created in a minimally complete and valid state. This is where validation comes into play. Any value that is not null-able gets passed in, along with FKs (keys or references) needed to ensure that if SaveChanges() was the next call after Create, the entity would be valid.
"Delete" methods similarly come in here to manage validating data state/authorization, and apply consistent behaviour. (hard vs. soft delete, auditing, etc.)
I don't use "Update" methods. Updates are handled by DDD methods on the entity itself. Controllers define the unit of work, use the repository to retrieve the entity(ies), call the entity methods, then commit the unit of work. Validation can be done at the entity level, or via a Validator class.
In any case, that is just a summary of one approach out of 10+ you may get out there, and hopefully highlights some things to consider with whatever approach you take. My emphasis when it comes to working with EF would be:
Keep it simple. (K.I.S.S. > D.N.R.Y)
Leverage what EF has to offer rather than attempting to hide it.
Complex, clever code ultimately leads to more code, and more code leads to bugs, performance issues, and makes it difficult to adjust for requirements you haven't thought of up-front. (Leading to more complexity, more conditional paths, and more headaches) Frameworks like EF have been tested, optimized, and vetted so leverage them.
I have my entity as:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I have my UserViewModel as
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I am using these as below in my controller:
//This is called from my view via ajax
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
MyRepository.UpdateUser(uv);
}
My UpdateUser in repository class is as below:
public void UpdateUser(User u)
{
var user = GetUserDetails(u.Id);
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
How can I correctly map UserViewModel in my controller to my entity
By using AutoMapper you can do something like:
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<UserViewModel , User>();
});
User u = config.CreateMapper().Map<User>(uv);
MyRepository.UpdateUser(u);
}
Or manually :
public void Save(UserViewModel uv)
{
User u = new User()
{
Id = uv.Id
Name = uv.Name;
Address = uv.Address;
};
MyRepository.UpdateUser(u);
}
Which is not good to do it manually if you change your view-model and your model then you should change your code also, but with Automapper you don't need to change the code.
EDIT1:
This is not good idea to use model-view in repository (DataAccess Core) so it would be better to keep your public void UpdateUser(User u) and don't change it, in outside it is better to pass user to UpdateUser not UserViewModel like what you have done before.
EDIT2:
In my opinion non of answered posts doesn't related to SOC (Separation on concerns) even mine...
1- When I passed UserViewModel I've violated the SOC ....
2- In the other side if I got User in Peresentation layer directly I also violated the SOC.
I think the best way is a middle layer as proxy....
Presentation <----> Proxy <----> Repository.
Your repository deals with objects of type User, so you need to map the values back to an instance of that type and then make the call.
Assuming you have a method to get the user called GetUser:
public void Save(UserViewModel uv)
{
var user = MyRepository.GetUser(uv.Id);
user.Name = uv.Name;
user.Address = uv.Address;
MyRepository.UpdateUser(user);
}
You can then save the changes in your repository class. You can attach the object to make sure there are no issues if the object was created in a different context:
public void UpdateUser(User u)
{
_context.Users.Attach(u);
_context.Entry(u).State = EntityState.Modified;
_context.SaveChanges();
}
You are doing the mapping of property values(view model->enity model) inside your repositories UpdateUser method. So use the view model class (UserViewModel) as the parameter type of that.
public void UpdateUser(UserViewModel u)
{
// Get the entity first
var user = GetUserDetails(u.Id);
// Read the property values of view model object and assign to entity object
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
Now from your Save method ,you can pass the view model object to this method.
This will fix your compile time error (which is your current problem in the question), but be careful about what classes you are using in what layers. If you are too much worried about using a view model class in your data access layer, you can do that in a middle service layer. But then you are getting the entity model in that layer and doing the update there.
Remember, there is no definite answer for that question. Use the approach you think is readable and consistent with the project/ team. Often times, i tend to use the term "Common DTO classes" than "View models" so i can peacefully pass those around to another layer. I keep them in a separate project (called Common DTO) and this will be cross cutting across other projects. That means i will add a reference to this Common DTO project in my Web/UI layer and my data access/service layer and use those as needed.
Extremely basic question about best practice in MVC when binding drop down lists.
This inst a real world example but a basic example that explains my question:
Take the following model
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Car Car { get; set; }
}
public class Car
{
public int ID {get;set;}
public string Make {get; set;{}
public string Model {get; set;}
}
Then assume that these get flattened into a view model:
public class IndexViewModel
{
public int PersonID;
public string Name;
public int SelectedCarID;
public SelectList<Cars> Cars;
}
In my constructor I have an index method:
[HttpGet]
public ActionResult Index()
{
var person = _ctx.People.FirstOrDefault(x=>x.ID == 1);
var vm = new IndexViewModel(){
Name = person.Name,
SelectedCarID = person.Car.ID,
};
return View(vm);
}
Now, Assume that the person that is returned from the context has NO car record when the page first loads.
The view has a line :
#Html.DropDownListFor(m=>m.SelectedCarID, Model.Cars)
When the form is submitted it is picked up by the action :
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
var person = _ctx.People.FirstOrDefault(x=>x.ID == model.PersonID);
var car = _ctx.Cars.FirstOrDefault(x=>x.ID == model.SelectedCarID);
person.Name = model.name;
person.Car = car;
_ctx.SaveChanges();
}
Now that is the way I have done it for ages, I started using EF back when LINQ to SQL was taking off and I have always created my models like that as I was under the imperssion that it was the recommended way.
After a discussion with another developer today I am not sure if this is stil the best way? It has always irked me that I need to do a lookup against the database to get the Car record out just so that I can update the record.
My questions are:
What is the best way to achive what I have described above?
Is the above correct?
Is there a better way to update the car entity against the person without doing a lookup (Preferably without including the foreign keys in the model)?
Is it better to just include the FKs in the model (Its not the way Ive been doing it bit it seems more sensible)?
Is there a way to bind the drop down to the car object (The guy I spoke to seemed to suggest you could but my knowlege of MVC/asp.net and furious googling seems to indicate that you cant)?
This really ins't the place for Best Practices sort of questions (that would probably be Code Review).
However some notes initially.
Keep your domain objects in the domain
The first thing that stood out to me was the SelectList<Car> property. Where it appears as your Car entity is actually a domain entity. A domain entity should not be exposed to the UI for multiple reasons.
Entity framework proxy classes monitor changes to properties that can be inadvertently saved.
Re-factoring of domain entities requires re-factoring of UI Code.
Domain entities typically contact properties you would not like exposed or otherwise.
Serialization of the Domain Entities will also serialize navigation properties and (mostly likely) cause circular reference errors.
Your question
Given the above you know have your answer, you will have to do a lookup for an entity based on your criteria from your View Model. Your view model should not have any understanding of the data context. It is in fact a View Model not a Domain Entity. By telling your View Model to interact with your data contexts you have no separation between your Data Access layers and your Presentation layers.
Don't make your controller manage data access as well
Your controller has a lot of work to-do, managing data access shouldn't be one of them. Doing so you have infarct coupled your Presentation Layer with your Data Access layer. Now as this is an example its easy to forgive however re factoring your data access layer will have direct consequences to your Presentation layer. I would suggest places a Services layer in between your data access layer and the presentation layer.
Ok All this in practice how does it look.
This is my personal approach here but will look at decoupling the data layer from the Presentation layer, no domain objects passed to the Presentation layer and using services to broker the transactions to the data layer.
Sample Service
This service is responsible for handling the interaction between the data layer and presentation (note mock repositories).
public class SampleService
{
public SampleService()
{
_dbContext = new SampleContext();
}
readonly SampleContext _dbContext;
public virtual Person GetPersonById(int id)
{
return _dbContext.Persons.FirstOrDefault(x => x.ID == id);
}
public virtual Car GetCarById(int id)
{
return _dbContext.Cars.FirstOrDefault(x => x.ID == id);
}
public virtual IList<Car> GetAllCars()
{
return _dbContext.Cars.ToList();
}
public virtual void UpdatePerson(Person person)
{
if (person == null)
throw new ArgumentNullException(nameof(person));
_dbContext.SaveChanges();
}
public virtual void UpdateCar(Car car)
{
if (car == null)
throw new ArgumentNullException(nameof(car));
_dbContext.SaveChanges();
}
}
Does this appear to be more work, absolutely does but better to implement your service now than have to do it later. What we also achieve is one location to update if we wish to change any queries or interaction methods.
IndexViewModel
As we have agreed we are no longer passing the car object to the SelectList. Infact we only need to construct a basic IList<SelectListItem> and populate this from our controller.
public class IndexViewModel
{
public IndexViewModel()
{
AvailableCars = new List<SelectListItem>();
}
public int PersonID { get; set; }
public string Name { get; set; }
public int SelectedCarId { get; set; }
public IList<SelectListItem> AvailableCars { get; set; }
}
Controller
Now our controller is pretty simple to wire up.
[HttpGet]
public ActionResult Index()
{
var person = sampleService.GetPersonById(1);
var model = new IndexViewModel
{
Name = person.Name,
PersonID = person.ID,
SelectedCarId = person.Car.ID
};
model.AvailableCars = sampleService.GetAllCars()
.Select(car => new SelectListItem
{
Text = $"{car.Make} - {car.Model}",
Value = car.ID.ToString()
})
.OrderBy(sli => sli.Text)
.ToList();
return View(model);
}
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
var person = sampleService.GetPersonById(model.PersonID);
if(person != null)
{
person.Name = model.Name;
//only update the person car if required.
if(person.Car == null || person.Car.ID != model.SelectedCarId)
{
var car = sampleService.GetCarById(model.SelectedCarId);
if (car != null)
person.Car = car;
}
sampleService.UpdatePerson(person);
}
return View();
}
View Drop Down list
#Html.DropDownListFor(m => m.SelectedCarId, Model.AvailableCars)
If you compare your code to my code I have actually added more code to the solution, however removes a lot of coupling and dependencies that could become hard to manage in larger applications.
Now back to your original questions.
Is there a better way to update the car entity against the person without doing a lookup (Preferably without including the foreign keys
in the model)?
No, you should be doing a lookup for that entity (car) outside of the Model. The model should not be aware of the data context.
Is it better to just include the FKs in the model (Its not the way Ive been doing it bit it seems more sensible)?
NO, your model should not be aware of the data context, therefore you do not need to define foreign keys (in a data context sense) leave that to your controller and services.
Is there a way to bind the drop down to the car object (The guy I spoke to seemed to suggest you could but my knowlege of MVC/asp.net
and furious googling seems to indicate that you cant)?
You could, but you don't want to. Our Car entity is a domain entity and we dont want to expose the entity to the UI (Presentation). Instead we will use other classes to expose what properties are bound. In this example a simple IList<SelectListItem> was more than sufficient.
I built a very simple MVC3 application to do a little demo, but I'm running against a problem; I return an entity to my view, edit it and then post it back, but in this process my entity loses it's change-tracking capabilities. When I return the entity to my view it's still an entity framework proxy class, but when it comes back from my view it's a 'Person' class (the entity is called person).
Here's my repository class:
public class PersonRepository : IPersonRepository
{
public EfContext Uow { get; set; }
public PersonRepository(IUnitOfWork uow)
{
Uow = uow as EfContext;
}
// yada yada yada
public void Add(Person person)
{
Uow.Persons.Add(person);
}
}
This entity is sent to my view that has a simple form, created with Html.EditorForModel. After that I post it back to this method:
[HttpPost]
public ActionResult Edit(Person person)
{
if (ModelState.IsValid)
{
_personRepository.Add(person);
_personRepository.Uow.Commit();
return RedirectToAction("Index");
}
return View(person);
}
And tada, it's no longer a tracked proxy class. This results in a primary key violation because entity framework is trying to add my object as a new object, where I just want entity framework to detect the changes and create an update statement instead. Oh by the way, the Commit method in the code above just calls SaveChanges(), here's the class:
public class EfContext : DbContext, IUnitOfWork
{
public DbSet<Account> Accounts { get; set; }
public DbSet<Person> Persons { get; set; }
public void Commit()
{
SaveChanges();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
By the way, this is my entity class:
public class Person
{
[HiddenInput(DisplayValue = false)]
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Account> Accounts { get; set; }
}
Does anyone know how to fix this? I had this working before as far as I can remember, I just don't know how.
Thanks in advance!
I assume that you're creating your repository per request (in the constructor or passed in as a dependency by your IoC framework or something). In that case, the entity that you receive in your controller method is indeed not tracked, because it was created in a different request. You have to "attach" it to the context and mark it as modified so EF knows it has changed and it needs to be saved to the DB
Define an Update method in your repository that will attach the entity and mark it as modified.
public class PersonRepository : IPersonRepository
{
// yada yada yada
public void Add(Person person)
{
Uow.Persons.Add(person);
}
public void Update(Person person)
{
Uow.Persons.Attach(person);
Uow.Entry(person).State = EntityState.Modified;
}
}
[HttpPost]
public ActionResult Edit(Person person)
{
if (ModelState.IsValid)
{
_personRepository.Update(person);
_personRepository.Uow.Commit();
return RedirectToAction("Index");
}
return View(person);
}
There is a 3rd solution here, which I believe is the most failsafe one.
You can configure your MVC action to receive an id integer. Then, using that integer, you retrieve the entity from the database. Then you call the UpdateModel method on the controller. This will bind the new form values to your existing object while it doesn't change the unchanged properties and properties that may not have been in the form.