I have created a database in SQL Server and have used Entity Framework to create a model in my C# MVC 5 project. In my models I am using System.ComponentModel to give a DisplayName to several properties (or columns). Example:
namespace ProjectTracking.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
public partial class EmployeeType
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public EmployeeType()
{
this.Employees = new HashSet<Employee>();
}
[DisplayName("Employee Type ID")]
public int PK_EmployeeTypeID { get; set; }
[DisplayName("Employee Type Name")]
public string EmployeeTypeName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Employee> Employees { get; set; }
}
}
The problem that I've run into is that, if I update the database and update my model, I lose all of this information as all of the model classes are regenerated. Is there a way to persist this information?
EF is using T4 templates to generate the code. You should never edit these files, because they could be regenerated anytime when you open the designer.
There is a way to do this, using the MetadataTypeAttribute`
EF generates the models as partial classes, so you can add another part in a different file, which won't be regenerated and a the MetadataTypeAttribute to it. This will point to yet another class in which you can duplicate the properties and supply the DisplayNameAttribute. Like this:
[MetadataType(typeof(EmployeeTypeMetaData))]
public partial class EmployeeType { }
public class EmployeeTypeMetaData
{
[Required(ErrorMessage = "Title is required.")]
public object Title;
// etc
}
This would the job... However:
You should not use this for the following reasons:
This will give you a maintenance nightmare. Any time your model changes you need to change the metadata also
You should never use your ef models directly in views of some kind. Use DTO objects in between and map between the ef models and the DTO. Mapping can be done either by hand of by a tool like AutoMapper
Your best option is to not use Entity Framework entities directly as View Models. Instead create view models that represent what needs to be displayed and map the values from the entities onto the view models (using AutoMapper makes this a cake walk).
What do you mean by "directly as View Models"
Your code probably looks like like:
public class MyContextDb
{
public DbSet<EmployeeType> EmployeeTypes { get; set; }
}
public class MyController
{
public ActionResult Index()
{
using (var db = new MyDbContext)
{
var emp = db.EmployeeTypes.FirstOrDefault();
return View(emp); // <-- passing EF entity as view model
}
}
}
Instead
public EmployeeTypeVM
{
// Properties you want to expose and annotate
}
using (var db = new MyDbContext)
{
var emp = db.EmployeeTypes.FirstOrDefault();
var vm = Mapper.Map<EmployeeTypeVM>(emp);
return View(vm); // <-- passing view model
}
I always recommend this approach as you don't normally change the emp, just the vm with logic and an accidental SaveChanged() doesn't affect real data.
Related
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?
My team uses Db first design.
We create the database, then create the model using the Scaffold-DbContext command.
The problem is when we need to modify the model and then do a recreation.
public partial class UserInfo
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public DateTime RecordCreated { get; set; }
}
Upon invoking a Scaffold-DbContext with the -Force it will remove the [Required] from it.
Should I be looking at using a ViewModel, creating partial classes or what?
Very early on in using EF core 2.1 so any help would be greatly appreciated.
Joe
If you are using database first, you make the database column required (NOT NULL), and then run scaffolding again, not the other way round. When scaffolding, you can choose to generated Attributes over fluent configuration, if you do that, you will get the "Required" attribute added (for reference types).
The switch for Scaffold-Dbontext is -DataAnnotations
https://learn.microsoft.com/en-us/ef/core/miscellaneous/cli/powershell#scaffold-dbcontext
Use EF Core Power Tools, it generates a partial method for OnModelCreating, and then use the fluent API in a new partial class to set the Required option instead of attributes.
I understand that it was created DB first but after initial creation of the models if you do model changes before changes in db it would be best to create migrations from code that way all the changes to the models will be replicated in the database.
As pointed out by ErikEJ this won't work:
You could use a metadata class along with a partial class (example
copied from doc):
using System;
using System.Web.DynamicData;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
[MetadataType(typeof(UserInfoMetaData))]
public partial class UserInfo
{
}
public class UserInfoMetaData
{
[Required()]
public object FirstName;
}
This would then sit in a separate file, that won't be touched by
code-gen. Note that you don't need to add all properties to the
metadata class and their type doesn't matter - but the names must
match.
There are some ways however how to make it work, see this SO item which itself is based on this ASP.NET Forums question. In the ASP.net link, there is also a suggestion to make the validation on the view model instead of the data classes. So that might be a possibility to consider.
Using EF database-first, is it possible to create a duplicate of one of the classes, such that any query made comes back with an additional filter?
As an example: Given a class
public partial class Person
{
public Person()
{
this.Job= new HashSet<Appointments>();
}
public int PersonID { get; set; }
public int JobID { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public virtual ICollection<Appointments> Appointments { get; set; }
}
Is it possible to construct a duplicate of the class in some way that functions like the existing class, but will only return results applied a "where Forename = 'David')
I can't overwrite the existing class (both cases need to be kept, and it'll be overwritten anyway)
My first thought was to simply create a seperate static class with methods that return an IQueryable< Persons>, but to then call that later, the context has been disposed - I don't think you can attach it to a new context?
The best you could do would be to add a function to your DbContext, in a partial class, that returns an IQueryable<Persons> with the filter already applied.
The partial class should have the same name as your actual context class. Any code in the partial class will be merged with the Database-First generated class, as if they were in the same file. It also won't get touched or overwritten by the code-generator if you regenerate the context. You can use this same concept to extend all kinds of code-generated classes (this is exactly the kind of use-case that partial classes were designed for).
public partial class MyDbContext
{
public IQueryable<Persons> FilteredPersons()
{
return this.Persons.Where(p => p.Forename =="David");
}
}
Then you can call it like this:
using (var myContext = new MyDbContext())
{
var query = myContext.FilteredPersons().Where(...some additional filter...);
var results = query.ToList();
}
You could probably also rig something up with an IDBCommandInterceptor, but that would be huge, hacky, ugly, and beyond the scope of a simple answer like this.
A few queries on EF.
Questions -
Should the methods that directly use database context object be part of Controller classes or Models?
ContactManagerContext.cs (which I am considering as a DAL layer?) Is my assumption correct?
Where should the ContactManager class be placed? Model or DAL? It is currently part of the Model class.
will add more questions
This is how I have structured the classes - Models and Controllers.
Please review and comment on if the code is structured correctly or not and how it can be improved.
Model class (Contact.cs):
using Contact_Manager.DAL;
namespace Contact_Manager.Models
{
public class Contact
{
[Key]
public int ContactId { get; set; }
[Required, MaxLength(100)]
public string FirstName { get; set; }
[Required, MaxLength(100)]
public string LastName { get; set; }
public string EMail { get; set; }
public string Phone { get; set; }
public string BusinessName { get; set; }
}
public class ContactManager
{
ContactContext db = new ContactContext();
public IEnumerable<Contact> ContactList(int? selectedContact)
{
IQueryable<Contact> contacts = db.Contacts;
return contacts.ToList();
}
}
}
ContactManagerContext.cs (DAL)
------------------------------
using System.Data.Entity;
using System.Linq;
using Contact_Manager.Models;
namespace Contact_Manager.DAL
{
public class ContactContext : DbContext
{
public ContactContext()
: base("ContactMgrDBContext")
{
Database.SetInitializer<ContactContext>(new DropCreateDatabaseIfModelChanges<ContactContext>());
}
public DbSet<Contact> Contacts { get; set; }
}
}
ContactController.cs (Controller class):
using System.Web.Mvc;
using System.Linq;
using Contact_Manager.Models;
namespace Contact_Manager.Controllers
{
public class ContactController : Controller
{
//
// GET: /Contact/
public JsonResult ContactList()
{
ContactManager cMgr = new ContactManager();
IEnumerable<Contact> contactList = cMgr.ContactList(0);
//var contactsJson = JsonConvert.SerializeObject(contacts.ToList());
return Json(contactList, JsonRequestBehavior.AllowGet);
}
public ActionResult Index()
{
return View();
}
}
}
The MVC pattern is one of the most misunderstood architecture patterns.
Also if it is used very often in UI it is a more general approach. The common usage has to be separated from the aim to adress different responsibilities.
The best way to explain MVC is to see it as a pattern that separates responsibilities AND the collaboration between them in ONE layer. So you may have MVC in the UI-Layer, but also in the DAO-Layer.
For example in the UI-Layer a model object is an object that holds the state of a UI-Component. The View-Object is the UI-Component that holds logic to draw itself on base of the model object state. The Controller retreives events from different sources and orchestrates communication between model and view.
In the DAO-Layer the model object is a part of a database state (often a row). Think of an ORM-Object. The view object is the representation for the "next" layer. The controller is the DAO that orchestrates mappings and changes.
In general you need something that holds a STATE (model). Then you need an external representaion of the STATE to PUBLISH (view) avoiding coupling to the STATE. After all you have to have BEHAVIOUR (controller) orchestrate changes and mappings.
The approach to see MVC as layers may work for small projects. In larger projects you will face problems in maintainability because you have definitely MORE than three responsibilities. And if you only have three layers you will mix responsibilities lacking the Single Responsibility Principle.
To answer your questions:
No. Write a separate DAO for that. The controller and/or the model should use that.
No. See 1.
If we talk about MVC the controller is the controller. Nothing else. If we talk about layers the controller seems to mix responsibilities of UI, Model maybe DAO. So the assignment is ambiguous.
You can you the three Layers' model in the Controller part. the Controller will be the highest layer, and it will "talk" with the BL and so on.
The Models suppose to be simple and clean.
I'm using EF5 to produce a model from an existing DB structure. I map Insert/Update/Delete Stored Procedures to the entities. This is working fine.
What I would like to do next is pass a UserId to these SPs as a parameter but not have the UserId as a column within the underlying table (the SPs will utilize this parameter). I have lots of entities. Is it possible to somehow add a property that will always be added back in even after updating the model from the DB?
Many Thanks
If you are using EDMX to generate the Entity Framework object model, your classes are partial and you can add properties to the partial classes of your entities which will survive database regeneration.
If you have a ParticularEntity table in the DB and referenced in the EDMX, you may add a partial class file ParticularEntity.Augments.cs (the name is for your reference, and you can have multiples as normal with partial classes) to your project and within it
public partial class ParticularEntity
{
public string UserId { get; set; }
public void DoSomething(string userId)
{
someFunctionThatYouWantToNotBeAnExtension();
}
}
Alternatively, you could write a set of extension methods which your maps utilize. I don't think that's as clean as extending the EF classes with partials, though.
Entity created by EF are partial class so you can extend that class with your custom properties
YourEntity.cs //created by EF
public partial class YourEntity
{
public string Name { get; set; }
...
}
YourEntityExtended.cs // created by you
public partial class YourEntity
{
public int Id { get; set; }
}