KushalSeth
KushalSeth

Reputation: 4649

How to hide auto incremented property while creating model in Entity Framework code-first

In Entity Framework, I have created a Department class which has an auto-incremented DepartmentId, and ICollection of Employees.

I don't want anyone see these two properties while creating the object of Department class. Can anyone help?

I tried using access modifier but that didn't work.

public class Department
{
    public int DepartmentId { get; set; }

    public string DepartmentName { get; set; }

    public ICollection<Employee> Employees { get; set; }
}


using (var ctx = new EmployeeContext())
{
    var department = new Department()
        {
                DepartmentName = "Engineering",
        };

    ctx.Departments.Add(department);
    ctx.SaveChanges();
}

When a user is doing

var department = new Department()

then department.DepartmentId and department.Employees should not be available.

Upvotes: 0

Views: 183

Answers (3)

Steve Py
Steve Py

Reputation: 34773

Backing fields are an option, however I don't recommend them for PKs given you are likely going to want to be able to select data by PK and the PK won't be exposed by the entity. For instance you might want to check child data using:

var employeesInDepartment = context.Employees
    .Where(x => x.Department.DepartmentId == departmentId)
    .ToList()

or

var employeesInDepartment = context.Departments
    .Where(x => x.DepartmentId == departmentId)
    .SelectMany(x => x.Employees)
    .ToList();

This won't be available if your Department ID is completely hidden. You can leverage .Find which will use the mapped PK, however, this is less practical because Find will always load the entity, where Select can be more selective about the data you want to retrieve. You'll also want PKs to send to the UI so that you can associate data coming back to their respective entities on the return trip. For instance when I create a new employee, it will be associated to a department. I send back a DepartmentID to associate, not disconnected entities. Sending entities is a performance and security trap, and even if you did want to serialize entities across, private backing fields would not be transferred so a Department entity coming back would lack any PK, rendering it useless.

Instead of trying to hide the property, you can guard the Setters by making them Private.

public class Department
{
    public int DepartmentId { get; private set; }
    // ...
    public virtual ICollection<Employee> Employees { get; private set; } = new List<Employee>();
}

This way you can still take advantage of accessing these properties in Linq expressions without implying they can be set. Collection references should never expose setters to avoid issues where someone tries clearing a child collection by setting the collection to a new List.

Note: This solution still will not allow you to receive entities from the client because the private setter will prevent deserialization. This is a good thing, as it will help ensure that your project does get corrupted to accept entities from the client. I cover the implications of this here.

Upvotes: 0

Stefan Jovanchevski
Stefan Jovanchevski

Reputation: 357

You can use backing fields feature.

Entity framework core

public class Department
{
    private int _departmentId { get; set; }

    public string DepartmentName { get; set; }

    private ICollection<Employee> _employees { get; set; }
}

public class Employee
{
    private int _employeeId { get; set; }

    public Department Department;
}

and now in your DbContext class

protected override void OnModelCreating(ModelBuilder modelBuilder)
{

    modelBuilder.Entity<Department>().HasKey("_departmentId");
    modelBuilder.Entity<Department>().Property<int>("_departmentId");
    modelBuilder.Entity<Department>(c =>
        c.HasMany(typeof(Employee), "_employees")
            .WithOne("Department")
    );

    modelBuilder.Entity<Employee>().HasKey("_employeeId");

    base.OnModelCreating(modelBuilder);
}

Entity framework 6.2.0

public class Employee
{
    private int _employeeId { get; set; }

    public Department Department { get; set; }

    public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
    {
        public EmployeeConfiguration()
        {
            HasKey(d => d._employeeId);
        }
    }
}

public class Department
{
    private int _departmentId { get; set; }

    public string DepartmentName { get; set; }

    private ICollection<Employee> _employees { get; set; }

    public class DepartmentConfiguration : EntityTypeConfiguration<Department>
    {
        public DepartmentConfiguration()
        {
            HasKey(d => d._departmentId);
            Property(d => d._departmentId);
            HasMany(d => d._employees).WithOptional(e => e.Department);
        }
    }
}

and now in your DbContext class

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{

    modelBuilder.Configurations
        .Add(new Department.DepartmentConfiguration())
        .Add(new Employee.EmployeeConfiguration());

    base.OnModelCreating(modelBuilder);
}

I had tried this code and it works.

Upvotes: 0

Masoud Andalibi
Masoud Andalibi

Reputation: 3228

If I understand correctly when you want to achieve is abstracting the POCO Department Class.

Well you can try creating a ViewModel for example:

public class DepartmentViewModel
{
    public string DepartmentName { get; set; }
}

Then Show or Get Database base this ViewModel, However if you want to Get the POCO Object from your for example : Dbset.Find() method or vice versa, you need to fill the object base on other object OR use Mappers such as AutoMapper. lets say you have an object from DepartmentViewModel called vm like below:

var vm = new DepartmentViewModel(); //<---- this is an example
var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<DepartmentViewModel, Department>();
});
IMapper mapper = config.CreateMapper();
//entity is a POCO Department Object. 
var entity = mapper.Map<DepartmentViewModel, Department>(vm);
//here you can pass the entity to EF for insert. 
using (var ctx = new EmployeeContext())
{
    ctx.Departments.Add(entity);
    ctx.SaveChanges();
}

Then as explained you can easily map it using Automapper, as you can see entity is now your Department object and you can do the vice versa by changing the DepartmentViewModel with Department in config.

And don't forget to download AutoMapper from nuget:

Install-Package AutoMapper -Version 8.1.1

Upvotes: 1

Related Questions