There are tons of samples on the net of how easy it is to persist objects with EntityFramework.
Most of those examples use the simplest POCO structures, that have list of C# automatic properties with public getters and setters, like this model of a person (Owner) and his pets (ICollection<Pet>):
Well, this is a working design. But think of how you would create instances of such classes. You create an instance, and then you assign all the required properties in your code. What if by accident you forget to assign one of the required properties?! - The answer is: it might take a while to find such bug in your application.
Another thing, which I don't like, is that such design allows user to fetch object from database and change it's ID, which is bad, we should never allow someone to change entity's ID.
We might also want to control other scalar property values, which are defined by our domain. But such design of data structures simply does not give us a certain amount of control over data, it forces us to put all the domain-validation logic outside of model classes.
Constructors and access modifiers come to rescue.
Let's modify the previous example:
We added constructors to both Owner and Pet class. Constructors in both classes take parameters for all required properties. Now we are unable to create model instances without filling all the required properties. We have also changed setter access of ID properties to internal.
The design looks okay now.
But we won't be able to persist these classes with EF.
In order to persist an instance of a class, EF requires us to declare parameterless constructor, let's define one:
Now we're able to persist classes with EF.
But there're some limitations conserning EF lazy loading feature. It won't work with such design.
EF documentation states, that we should mark association properties as virtual. But take a look, we have internal setter for ICollection<Pet> Pets property. We also have internal parameterless constructor for Owner and Pet classes. To make lazy loading working, EF subclasses your entity class and overrides virtual properties. With internal access level of constructor and association property, EF will not be able to override anything.
protected internal access level comes to rescue:
protected internal access level acts as protected OR internal. This way it is allowed for the subclass to override virtual members.
Most of those examples use the simplest POCO structures, that have list of C# automatic properties with public getters and setters, like this model of a person (Owner) and his pets (ICollection<Pet>):
public class Owner
{
public int Id { get; set; }
public string FullName { get; set; }
public ICollection<Pet> Pets { get; set; }
}
public class Pet
{
public int Id { get; set; }
public string Name { get; set; }
}
{
public int Id { get; set; }
public string FullName { get; set; }
public ICollection<Pet> Pets { get; set; }
}
public class Pet
{
public int Id { get; set; }
public string Name { get; set; }
}
Well, this is a working design. But think of how you would create instances of such classes. You create an instance, and then you assign all the required properties in your code. What if by accident you forget to assign one of the required properties?! - The answer is: it might take a while to find such bug in your application.
We might also want to control other scalar property values, which are defined by our domain. But such design of data structures simply does not give us a certain amount of control over data, it forces us to put all the domain-validation logic outside of model classes.
Constructors and access modifiers come to rescue.
Let's modify the previous example:
public class Owner
{
public Owner(int id, string fullName)
{
this.Id = id;
this.FullName = fullName;
this.Pets = new List<Pet>();
}
public int Id { get; internal set; }
public string FullName { get; set; }
public ICollection<Pet> Pets { get; internal set; }
}
public class Pet
{
public Pet(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; internal set; }
public string Name { get; set; }
}
{
public Owner(int id, string fullName)
{
this.Id = id;
this.FullName = fullName;
this.Pets = new List<Pet>();
}
public int Id { get; internal set; }
public string FullName { get; set; }
public ICollection<Pet> Pets { get; internal set; }
}
public class Pet
{
public Pet(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; internal set; }
public string Name { get; set; }
}
We added constructors to both Owner and Pet class. Constructors in both classes take parameters for all required properties. Now we are unable to create model instances without filling all the required properties. We have also changed setter access of ID properties to internal.
The design looks okay now.
But we won't be able to persist these classes with EF.
In order to persist an instance of a class, EF requires us to declare parameterless constructor, let's define one:
public class Owner
{
internal Owner() {}
public Owner(int id, string fullName)
{
this.Id = id;
this.FullName = fullName;
this.Pets = new List<Pet>();
}
public int Id { get; internal set; }
public string FullName { get; set; }
public ICollection<Pet> Pets { get; internal set; }
}
public class Pet
{
internal Pet() {}
public Pet(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; internal set; }
public string Name { get; set; }
}
{
internal Owner() {}
public Owner(int id, string fullName)
{
this.Id = id;
this.FullName = fullName;
this.Pets = new List<Pet>();
}
public int Id { get; internal set; }
public string FullName { get; set; }
public ICollection<Pet> Pets { get; internal set; }
}
public class Pet
{
internal Pet() {}
public Pet(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; internal set; }
public string Name { get; set; }
}
Now we're able to persist classes with EF.
But there're some limitations conserning EF lazy loading feature. It won't work with such design.
EF documentation states, that we should mark association properties as virtual. But take a look, we have internal setter for ICollection<Pet> Pets property. We also have internal parameterless constructor for Owner and Pet classes. To make lazy loading working, EF subclasses your entity class and overrides virtual properties. With internal access level of constructor and association property, EF will not be able to override anything.
protected internal access level comes to rescue:
public class Owner
{
protected internal Owner() {}
public Owner(int id, string fullName)
{
this.Id = id;
this.FullName = fullName;
this.Pets = new List<Pet>();
}
public int Id { get; internal set; }
public string FullName { get; set; }
public virtual ICollection<Pet> Pets { get; protected internal set; }
}
public class Pet
{
protected internal Pet() {}
public Pet(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; internal set; }
public string Name { get; set; }
}
{
protected internal Owner() {}
public Owner(int id, string fullName)
{
this.Id = id;
this.FullName = fullName;
this.Pets = new List<Pet>();
}
public int Id { get; internal set; }
public string FullName { get; set; }
public virtual ICollection<Pet> Pets { get; protected internal set; }
}
public class Pet
{
protected internal Pet() {}
public Pet(int id, string name)
{
this.Id = id;
this.Name = name;
}
public int Id { get; internal set; }
public string Name { get; set; }
}
protected internal access level acts as protected OR internal. This way it is allowed for the subclass to override virtual members.
No comments:
Post a Comment