Alternative to multiple inheritance in Entity Framework - c#

I would like to use inheritance to enforce consistency in Entity Framework model classes. For example, if X different models all have an address, they could inherit from:
public abstract class EntityAddress
{
[MaxLength(400)]
[Display(Name = "Street address")]
[DataMember]
public string AddressLine1 { get; set; }
[MaxLength(400)]
[Display(Name = "Address line 2")]
[DataMember]
public string AddressLine2 { get; set; }
[MaxLength(100)]
[Display(Name = "City")]
[DataMember]
public string City { get; set; }
[MaxLength(100)]
[Display(Name = "State")]
[DataMember]
public string State { get; set; }
[MaxLength(40)]
[Display(Name = "Zip code")]
[DataType(DataType.PostalCode)]
[DataMember]
public string ZipCode { get; set; }
}
This would ensure that all addresses are consistently implemented across the product (yes, if a model has two addresses, we have an issue, but I'll wave that away for the purposes of this discussion).
I would also like the ability to have a class use an unlimited number of these concepts. For example, if a model has an address and a full name, it could do this:
public class Customer : EntityAddress, EntityFullName
{
}
Multiple inheritance, however, is not supported in C#.
Does anyone have any ideas on good alternate methods to achieve what I am trying to do here? I don't believe interfaces will work because I can't embed the attributes with the properties. I don't believe a class property will work because I want the columns in the DB associated with the base classes to be in the same table as the model class properties.

Complex types appear to be an answer to this question (credit to Ivan Stoev).
https://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-2-complex-types

You could wrap in a class. Its not pretty, but it will achieve similar results
public class Customer
{
public EntityAddress address {get;set;}
public EntityFullName fullname {get;set;}
}

Related

Partial server side validation for complex property

In ASP.NET Core MVC app, I need to skip model validation for certain complex property in controller's action.
Let's say I have a following model structure:
public class Person
{
public int PersonID { get; set; }
public PersonalInfo PersonalInfo { get; set; }
public ContactInfo ContactInfo { get; set; }
public Person()
{
PersonalInfo = new PersonalInfo();
ContactInfo = new ContactInfo();
}
}
public class PersonalInfo
{
[Required(ErrorMessage = "First name is required")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last name is required")]
public string LastName { get; set; }
}
public class ContactInfo
{
[Required(ErrorMessage = "Phone is required")]
public string Phone { get; set; }
[Required(ErrorMessage = "Email is required")]
public string Email{ get; set; }
}
In post action, I would like to skip validation for ContactInfo, although it is a part of model and is submitted. Something like this:
[HttpPost]
public ActionResult SavePerson(Person model)
{
ModelState.Remove("ContactInfo");
if (ModelState.IsValid)
{
(...)
}
}
This would work if ContactInfo was simple (is "scalar" correct term?) property.
I know that some of you would suggest me to use seperate viewmodels, but I do not think it is applicable in this case (I'm trying to create a form with multiple steps, and all the data has to be on form in order to be submitted in order to be preserved between steps...)
Also, I guess I could use ModelState.Remove for each property of ContactInfo class, but it seems repetitive and difficult to maintain, especially because my classes contain much more properties.
ModelState.Remove("ContactInfo");
Seems that the ModelState doesn't contain the "ContactInfo" key, so it will not work. If you want to disable the validation in the child class, as you guess, you need to remove all properties in it, such as:
ModelState.Remove("ContactInfo.Phone");
ModelState.Remove("ContactInfo.Email");
but it seems repetitive and difficult to maintain, especially because my classes contain much more properties.
It is indeed repetitive when there are many properties, but you can use reflection to simplify it.
foreach (var property in model.ContactInfo.GetType().GetProperties())
{
ModelState.Remove("ContactInfo." + property.Name);
}
If you have arrays try following. If you do not use XmlElement with an array Xml Serialization requires two Xml Tags. Using XmlElement requires only one tag. :
[XmlElement()]
public List<PersonalInfo> PersonalInfo { get; set; }
[XmlElement()]
public List<ContactInfo> ContactInfo { get; set; }

EFCF 6 Complex Type mapping failing after refactoring toward Value Object pattern

I was refactoring a working implementation of a Complex Type class to apply the Value Object pattern which would make it an immutable type.
I got that working, I had thought, until I tried to set up the migrations for these changes (the main change that I think would matter to EF is that I changed some properties over to being get-only where they previously had setters). I am currently getting the following errors when I run Update-Database:
PM> update-database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Applying explicit migrations: [201701010745393_initial].
Applying explicit migration: 201701010745393_initial.
Running Seed method.
System.Data.Entity.Core.EntityCommandCompilationException: An error occurred while preparing the command definition. See the inner exception for details. ---> System.Data.Entity.Core.MappingException:
(29,10) : error 3004: Problem in mapping fragments starting at line 29:No mapping specified for properties Contact.Address in Set Contacts.
(46,10) : error 3004: Problem in mapping fragments starting at line 46:No mapping specified for properties Company.Address in Set Companies.
(56,10) : error 3004: Problem in mapping fragments starting at line 56:No mapping specified for properties CompanyLocation.Address in Set Locations.
This would appear to be defying what I have been reading about how EF handles Complex Types, in that it should map things automatically whenever it encounters entities which make use of a property that is a Complex Type. This was previously the case, however it is no longer working with the change to an immutable class. I am not sure why that is.
Here are the relevant classes pertaining to these error messages:
public class Company : PocoBase
{
[Required]
public Address Address { get; set; }
public virtual ICollection<Client> Clients { get; set; }
public virtual ICollection<CompanyLocation> Locations { get; set; }
[Required]
public string Name { get; set; }
}
public class CompanyLocation : PocoBase
{
[Required]
public Address Address { get; set; }
public virtual Company Company { get; set; }
[ForeignKey("Company")]
public Guid CompanyId { get; set; }
public string Description { get; set; }
public string Label { get; set; }
}
public class Contact : PocoBase
{
public Address Address { get; set; }
[Required]
public string CellNumber { get; set; }
public virtual Client Client { get; set; }
[ForeignKey("Client")]
public Guid ClientId { get; set; }
public virtual Company Company { get; set; }
[ForeignKey("Company")]
public Guid CompanyId { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string OfficeNumber { get; set; }
}
And, of course, the all-important Address class, which is now causing problems!
[ComplexType]
public class Address
{
[Required]
public string City { get; }
[Required]
public string Country { get; }
[Required, StringLength(10, MinimumLength = 5)]
public string PostalCode { get; }
[Required, StringLength(2, MinimumLength = 2)]
public string State { get; }
[Required]
public string StreetAddress { get; }
public string UnitNumber { get; }
public Address(string street, string city, string state, string zip, string unit = null) : this("United States", street, city, state, zip, unit) { }
public Address(string country, string street, string city, string state, string zip, string unit = null)
{
VerifyZipCodeFormat(zip);
Country = country;
StreetAddress = street;
City = city;
State = state;
PostalCode = zip;
UnitNumber = unit;
}
private static void VerifyZipCodeFormat(string zip)
{
if (zip.Length > 5)
if (zip[5] != '-')
throw new ArgumentOutOfRangeException(nameof(zip), zip[5], "Postal Code must be in the format of \"XXXXX\" or \"XXXXX-XXXX\"");
else if (zip.Length != 10)
throw new ArgumentOutOfRangeException(nameof(zip), zip.Length, "Postal Code must be either 5 or 10 characters, in either the format of \"XXXXX\" or \"XXXXX-XXXX\"");
}
public Address WithCity(string city)
{
return new Address(Country, StreetAddress, city, State, PostalCode, UnitNumber);
}
public Address WithCountry(string country)
{
return new Address(country, StreetAddress, City, State, PostalCode, UnitNumber);
}
public Address WithStateOrProvince(string state)
{
return new Address(Country, StreetAddress, City, state, PostalCode, UnitNumber);
}
public Address WithStreetAddress(string street)
{
return new Address(Country, street, City, State, PostalCode, UnitNumber);
}
public Address WithUnitNumber(string unit)
{
return new Address(Country, StreetAddress, City, State, PostalCode, unit);
}
public Address WithZipOrPostalCode(string zip)
{
VerifyZipCodeFormat(zip);
return new Address(Country, StreetAddress, City, State, zip, UnitNumber);
}
}
I have tried to do this mapping manually, in the same vein that I believe the EFCF Convention is - Address_PropertyName in the migrations file that EF does try to create when I run Add-Migration, but this did not work either.
Neither having the [ComplexTypeAttribute] nor using the modelBuilder.ComplexType() lines have solved these error messages, so I have to assume it is related to this being an immutable class now.
I had hoped that adding the [ColumnAttribute] with column names would solve the issue somehow, as that seemed to be indicated in this post, but that did not solve the issue either.
Neither having the [ComplexTypeAttribute] nor using the modelBuilder.ComplexType() lines have solved these error messages, so I have to assume it is related to this being an immutable class now.
That's correct. EF6 does not map get only properties and there is no way tp change that behavior with data annotations or fluent configuration (note that the behavior in EF Core is different, but still causes issues), so providing private setters will fix the issue:
[ComplexType] // optional
public class Address
{
[Required]
public string City { get; private set; }
[Required]
public string Country { get; private set; }
[Required, StringLength(10, MinimumLength = 5)]
public string PostalCode { get; private set; }
[Required, StringLength(2, MinimumLength = 2)]
public string State { get; private set; }
[Required]
public string StreetAddress { get; private set; }
public string UnitNumber { get; private set; }
// ...
}
Now, I know this is not equivalent to the original implementation which ensures the fields are set only during the construction. But in general I would suggest you to forget the OO principles, patterns and practices when modeling EF entities. Entity classes are basically DTOs representing database tables, records and relationships with no associated business logic. Also note that EF tracks entities using reference identity, so applying immutable principle on entity types will only cause you problems (fortunately that doesn't apply for complex types).

Entity Framework enum property is ignored by code first

Okay, the problem is this. I have multiple models that make use of enum properties, in all but one of them I've had no problem with this. In this model however code first does not recognise this property and will not create a column in the respective table. What's bizarre is that I have a very similar model to the non-functioning one that works fine, the only difference being that it's in a different namespace.
I'll add that:
My project is targeting .NET 4.5 (and indeed the functioning and non-functioning models are within the same project).
I'm using Entity Framework 5
My enums are defined publicly in the namespace, not nested within a class.
This eliminates the most common causes of problems when trying to use enums in EF.
Here are the code snippets, first the non-working one:
public enum Commodities
{
Test1,
Test2,
Test3
}
[Table("Suppliers")]
public class Suppliers
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Required]
[MaxLength(7)]
[Display(Name = "Vendor ID")]
public string VendorID { get; set; }
[Required]
[Display(Name = "Supplier Name")]
public string SupplierName { get; set; }
public Commodities Commodity { get; set; }
public bool Visible { get; set; }
}
Now here is one that works fine, only difference being that it's in a different namespace and class file (both sit in the same project and have identical using declarations):
public enum Commodities
{
Test4,
Test5,
Test6
}
[Table("Buyers")]
public class Buyer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Buyer ID")]
[Required]
public string BuyerID { get; set; }
[Display(Name = "Buyer Name")]
[Required]
public string Name { get; set; }
[Display(Name = "Windows Logon ID")]
public string WinUserID { get; set; }
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$", ErrorMessage = "Please enter a valid e-mail adress")]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
public Commodities Commodity { get; set; }
[Display(Name = "Active?")]
public Boolean IsActive { get; set; }
}
In the "Buyer" table code first correctly initialising the Commodity column and maps it to the enum in the model. In the "Suppliers" table however it's only initialised with the columns: VendorID, VendorName, and Visible with Commodity no where to be found. My question is why is this happening and what can I do to fix it? I'm at my wits end with this.
EDIT: Seems the issue might have to do with using two enums with the same name. Although the compiler has no complaints about ambiguity (and it shouldn't as they exist in different namespaces) it seems EF doesn't like it. Changing the name of one of the enums causes CF to recognise the property.

Multiple Attributes in one?

I have these properties (A,B) :
[DataType(DataType.MultilineText)]
[Required(ErrorMessage = "A is required"), DisplayName("A")]
[StringLength(Constants.MaximunStringSize)]
public string A { get; set; }
[DataType(DataType.MultilineText)]
[Required(ErrorMessage = "B is required"), DisplayName("B")]
[StringLength(Constants.MaximunStringSize)]
public string B { get; set; }
I can create a class that "inherits" all the attributes (DataType, Required, StringLength, DisplayName) and the set through its constructor?. By example:
[MyAttribute("A","A is required")]
public string A { get; set; }
[MyAttribute("B","B is required")]
public string B { get; set; }
In general, no.
However, for validation attributes, you could create your own validation attribute that contains all of the logic in the existing attributes.
To emulate [DataType], you'll need to implement IMetadataAware.
There is no multiple inheritance in C#, so no, you can't do this.
You can, however write your own Attribute that incorporates all the functionality of those attributes.

Can multiple classes share a single db table in ActiveRecord/NHibernate

C# w/Castle ActiveRecord
I've two (or more) classes in C# that I'd like to interact with the same database table. These two classes do NOT share a base class, so I can't use a Discriminator. I'd simply like to divide the table columns over two (or more) classes. Kind of the opposite of the purpose of the JoinedTable attribute. Can this be done?
To expand on my comment, you can logically group the columns in the active record object by inheriting multiple interfaces...
interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
}
interface IAddress
{
string AddressLine1 { get; set; }
string AddressLine2 { get; set; }
string City { get; set; }
string Province { get; set; }
string Country { get; set; }
}
class CustomerRecord : IPerson, IAddress // ActiveRecord object map to table...
{
// IPerson members...
public string FirstName { get; set; }
public string LastName { get; set; }
// IAddress members...
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string Province { get; set; }
public string Country { get; set; }
}
There are two strategies to map several classes to one table:
Inheritance: yes, I know you don't have it, but you could also use an interface as "base class". It doesn't make sense to have several (root) classes in the same table witch don't have anything in common.
Components: are the actual opposite of join. You can put parts of the table to separate classes. In this case, there is only one root entity which aggregates other classes.

Categories