EF CF Mapping complex relationship with Fluent API - entity-framework

I am trying to create the following constraint in my model so that a Tag object's TagType is valid. A valid TagType is one whose OperatingCompanyId matches the Tag's Website's OperatingCompanyId. I realize that this seems convoluted however it makes sense from a business standpoint:
An Operating Company has WebSites. Websites contain Tags. Tags have a TagType(singular). TagTypes are the same across Operating Companies, meaning that if one Operating Company has twenty TagTypes and five WebSites, those twenty TagTypes should be able to be used across all fives of those WebSites. I want to ensure that a Tag's TagType cannot be one associated with another OperatingCompany.
What is the best way to create this constraint in the model? Do I need to change my POCO, or use the Fluent API?
Thanks in advance!
[Table("OperatingCompanies")]
public class OperatingCompany : ConfigObject
{
public OperatingCompany()
{
WebSites = new List<WebSite>();
}
[Required(ErrorMessage = "Name is a required field for an operating company.")]
[MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters.")]
public string Name { get; set; }
public virtual ICollection<WebSite> WebSites { get; set; }
}
[Table("Websites")]
public class WebSite : ConfigObject
{
public WebSite()
{
WebObjects = new List<WebObject>();
}
[Required(ErrorMessage = "URL is a required field for a web site.")]
[MaxLength(100, ErrorMessage = "URL cannot exceed 100 characters for a web site.")]
[RegularExpression(#"\b(https?|ftp|file)://[-A-Za-z0-9+&##/%?=~_|!:,.;]*[-A-Za-z0-9+&##/%=~_|]", ErrorMessage = "The value entered is not a valid URL.")]
public string Url { get; set; }
public OperatingCompany OperatingCompany { get; set; }
[Required(ErrorMessage = "You must associate a web site with an operating company.")]
public Guid OperatingCompanyId { get; set; }
[InverseProperty("Website")]
public virtual ICollection<WebObject> WebObjects { get; set; }
}
[Table("Tags")]
public class Tag : ConfigObject
{
[Required(ErrorMessage = "Name is a required field for a tag.")]
[MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters for a tag.")]
public string Name { get; set; }
public TagType TagType { get; set; }
[Required(ErrorMessage = "You must associate a tag with a tag type.")]
public Guid TagTypeId { get; set; }
public WebSite WebSite { get; set; }
[Required(ErrorMessage = "You must associate a tag with a web site.")]
public Guid WebSiteId { get; set; }
}
[Table("TagTypes")]
public class TagType : ConfigObject
{
[Required(ErrorMessage = "Name is a required field for a tag.")]
[MaxLength(100, ErrorMessage = "Name cannot exceed 100 characters for a tag type.")]
public string Name { get; set; }
public OperatingCompany OperatingCompany { get; set; }
[Required(ErrorMessage = "You must associate a tag type with an operating company.")]
public Guid OperatingCompanyId { get; set; }
}

One way to enforce this constraint is to take advantage of the new validation feature introduced as part of new DbContext API in EF 4.1. You can write a custom validation rule to make sure that tag types for any given company's website are selected from the valid tag types for that company. The following shows how it can be done:
public abstract class ConfigObject
{
public Guid Id { get; set; }
}
public class OperatingCompany : ConfigObject, IValidatableObject
{
public string Name { get; set; }
public virtual ICollection<WebSite> WebSites { get; set; }
public virtual List<TagType> TagTypes { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var allTagTypes = (from w in WebSites from t in w.Tags select t.TagType);
if (!allTagTypes.All(wtt => TagTypes.Exists(tt => tt.Id == wtt.Id)))
{
yield return new ValidationResult("One or more of the website's tag types don't belong to this company");
}
}
}
public class WebSite : ConfigObject
{
public string Url { get; set; }
public Guid OperatingCompanyId { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
public OperatingCompany OperatingCompany { get; set; }
}
public class Tag : ConfigObject
{
public string Name { get; set; }
public Guid TagTypeId { get; set; }
public Guid WebSiteId { get; set; }
public TagType TagType { get; set; }
public WebSite WebSite { get; set; }
}
public class TagType : ConfigObject
{
public string Name { get; set; }
public Guid OperatingCompanyId { get; set; }
public OperatingCompany OperatingCompany { get; set; }
}
public class Context : DbContext
{
public DbSet<OperatingCompany> OperatingCompanies { get; set; }
public DbSet<WebSite> WebSites { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<TagType> TagTypes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Tag>().HasRequired(t => t.WebSite)
.WithMany(w => w.Tags)
.HasForeignKey(t => t.WebSiteId)
.WillCascadeOnDelete(false);
}
}
As a result, EF will invoke that validate method each time you call DbContext.SaveChanges() to save an OperatingCompany object into database and EF will throw (and abort the transaction) if the method yields back any validation error. You can also proactively check for validation errors by calling the GetValidationErrors method on the DbContext class to retrieve a list of validation errors within the model objects you are working with.
It also worth noting that since you use your domain model as also a View Model for your MVC layer, MVC will recognize and honor this Validation rule and you can check for the validation result by looking into the ModelState in the controller. So it really get checked in two places, once in your presentation layer by MVC and once in the back end by EF.
Hope this helps.

however... if I understand the purpose of MVC / EF it is to have that
business logic inside of the Model...
And what model do you mean? If you take ASP.NET MVC and EF you will end with three areas which are sometimes called model:
EF model - that is set of classes with their mapping to database
Model-View-Controller - model here means something (usually business logic) consumed by your controller to prepare data for view
View model - In ASP.NET MVC view model is class with data exchanged between controller and view
If I look at your classes I see first and third model coupled together (most of the time this is considered as a bad practice). Your understanding is correct but mostly in terms of second model which is not represented by your classes. Not every "business logic" can be represented by mapping. Moreover it is not a point of data layer to do business logic.
Your mapping partially works (tag type is related only to one operating company) but still your data layer doesn't enforce all your business rules. Data layer still allows web site to have assigned tag with tag type from different operating company and your business logic must ensure that this will not happen. Avoiding this in database would be complicated because it would probably require complex primary keys and passing operating company Id to every dependent object.

If I were you, I will use business layer to filter Tagtype instead of do such constraint in database. For me that approach may be easier.

Related

OData v4 Expanding Multiple One-Many

I'm creating an ASP.NET Core 3.1 Web API using OData v4.
I just made a GitHub repo here containing the entire solution, even the database project with dummy data.
Some blogs helped me along the way:
Experimenting with OData in ASP.NET Core 3.1
Supercharging ASP.NET Core API with OData
I've successfully created 3 basic endpoints that can be queried (Countries, Cities and Customers).
The Country and City endpoints work as expected, it is the Customer endpoint that causes some issues on $expand.
The Customer model looks like this (please note that I am currently using domain entities instead of DTO's because I want to get everything working smoothly first, before projecting them to DTO's):
public abstract class AppEntity : IAppEntity
{
[Key]
public int Id { get; set; }
}
public class Customer : AppEntity
{
public string Name { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public virtual City City { get; set; }
public string VAT { get; set; }
public virtual List<CustomerEmailAddress> EmailAddresses { get; set; }
public virtual List<CustomerNote> Notes { get; set; }
}
With the following models acting as navigation properties:
public class CustomerEmailAddress : AppEntity
{
public Customer Customer { get; set; }
public string EmailAddress { get; set; }
public bool IsPrimary { get; set; }
}
public class CustomerNote : AppEntity
{
public Customer Customer { get; set; }
public DateTime DateTime { get; set; }
public string Message { get; set; }
}
Most of my queries are successful:
Just the collection: https://localhost:44309/api/customer
Expanding the City: https://localhost:44309/api/customer?$expand=City
On of the one-many relationships: https://localhost:44309/api/customer?$expand=Notes
But as soon as I try to expand 2 or more one-many properties or expand all (?$expand=*), I get an exception:
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
Any clue where this exception might be coming from?
My EdmModel is defined as:
IEdmModel GetEdmModel()
{
var odataBuilder = new ODataConventionModelBuilder();
odataBuilder.EntitySet<Country>("Country");
odataBuilder.EntitySet<City>("City");
odataBuilder.EntitySet<Customer>("Customer");
return odataBuilder.GetEdmModel();
}

Nested Properties with Inheritance

Online shop I am working on has entity Order that has member DeliveryDetails.
The purpose of DeliveryDetails is to contain data which is specific to delivery method selected by user (e.g. Shipping or Pick Up From Store), while some details are common for all methods (e.g. Firstname, Lastname, PhoneNumber). I was thinking about structure similar to the following using inheritance:
public class Order {
// ....other props...
public DeliveryMethodType DeliveryMethodType { get; set; }
public DeliveryDetailsBase DeliveryDetails { get; set; }
}
public class DeliveryDetailsBase
{
public int Id { get; set; }
public string CustomerId { get; set; }
public Order Order { get; set; }
public int OrderId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string PhoneNumber { get; set; }
}
public class DeliveryDetailsShipping : DeliveryDetailsBase
{
public string Street { get; set; }
public string Building { get; set; }
public string Appartment { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public class DeliveryDetailsPickupFromStore : DeliveryDetailsBase
{
public string StoreCode { get; set; }
}
However, I can't figure out how to make DeliveryDetails prop be assigned to different type of delivery method details depending on what method customer selected and how to fit it in EntityFramework on ASP.Core.
Workarounds I have already tried:
-> (1). Creating "super class" contatining props for ALL delivery methods and populate in db only those that are needed for selected delivery method (selection via setting enum DeliveryMethodType). OUTCOME: works, but with 1 big and ugly table featuring multiple nulls.
-> (2). In Order, creating prop DeliveryDetails which in turn embraces DeliveryDetailsPickupFromStoreDATA & DeliveryDetailsShippingDATA. OUTCOME: works, but with several related tables and quite a lot of ugly code checking selected type from enum, instantiating specific subclass for chosen delivery method and setting to null other unused subclasses.
TO SUM UP: Is there any more elegant and feasible way to organize this?
Is there any more elegant and feasible way to organize this?
Keep it simple, and inheritance isn't usually simple. :)
As a general rule I opt for composition over inheritance. It's easier to work with. Given an order that needs to be delivered to an address or to a store:
public class Order
{
public DeliveryMethod DeliveryMethod { get; set; } = DeliveryMethod.None;
public virtual OrderDeliveryAddress { get; set; } // should never be null.
public virtual OrderDeliveryStore { get; set; } // not null if delivery mode = store.
}
public class Address
{
public string Street { get; set; }
public string Building { get; set; }
public string Appartment { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public class OrderDeliveryAddress
{
public virtual Order Order { get; set; }
public virtual Address Address { get; set; }
}
public class Store
{
public int StoreId { get; set; }
public virtual Address { get; set; }
}
public class OrderDeliveryStore
{
public virtual Order Order { get; set; }
public virtual Store Store { get; set; }
}
Where DeliveryMethod is an Enum. { None = 0, ToAddress, ToStore }
When an order is placed the operator can choose to deliver it to an address, selecting the address of the customer, or entering a new address record; or they can deliver it to a store which can also set the OrderDeliveryAddress to the address of the store. You can establish checks in the database/system to ensure that the data integrity for the delivery method and referenced OrderDeliveryAddress/OrderDeliveryStore are in sync and raise any mismatches that might appear.
One consideration would be that when it comes to deliveries, you will probably want to clone a new Address record based on the customer address, or store address as applicable at the time of ordering rather than referencing their current address record by ID. The reason would be for historical integrity. An order will have been delivered to the address at that point in time, and if a customer address or store address changes in the future, past orders should still show the address that order was delivered.
EF Core has only implemented Table Per Hierarchy (TPH) inheritance.
Table Per Type (TPT) is still an open ticket (not implemented).
Table Per Concrete Type (TPC) is also still an open ticket (not implemented).
So, if TPH meets your requirements, you can follow this guide.
Basically, one table will be used and an extra column called Discriminator will be used to determine which implementation the record corresponds to.
If you are just getting started with Entity, my recommendation would be to not use inheritance and just use nullable columns for data that may or may not be needed depending on the type.

Adding non-Identity tables to Identity server in ASP.NET core 2 ; do I need a different dbcontext?

I have looked and havent seen anything on this, most information relates to directly extending Identity tables.
I have extended Application User like so:
public class ApplicationUser : IdentityUser<long>
{
//[Key()]
//public long Id { get; set; }
[Required()]
[MaxLength(100)]
public string Password { get; set; }
[Required()]
[MaxLength(100)]
override public string UserName { get; set; }
[Required()]
[MaxLength(50)]
public string FirstName { get; set; }
[Required()]
[MaxLength(50)]
public string LastName { get; set; }
public virtual Organization Organization { get; set; }
}
public class ApplicationRole : IdentityRole<long>
{
}
along with other neccesary changes to ApplicationDbContext (to change the primary key to long). Other than that, its fairly standard Identity stuff. I add migrations, update; the usual tables are created plus Organization table because it's a navigation property. Organization itself has no navigation properties. Keep in mind, for the most part I was handed these classes as part of a project and am trying to work within the confines of what I've been given.
Now, I have several classes that I need to add, one as an example:
public class Event
{
[Key()]
public long Id { get; set; }
[Required()]
[MaxLength(200)]
public string Name { get; set; }
[Index]
public virtual Organization Organization { get; set; }
}
There are a handful of other something inter related classes. So in a standard code first core 2 app with no existing db, I would create the context class that derives from DbContext, whereas in Identity ApplicationDbContext (the default name) derives from IdentityDbContext.
So before I start breaking things, are there any concerns or special considerations before I do something like this?
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
public DbSet<Event> Events{ get; set; }
public DbSet<OtherClass> OtherClasses{ get; set; }
}
Note: I did find this post which seems to do what I am talking about but it is for MVC 5
You can just add the classes as properties, i.e. public DbContext<Class> Classes {get;set;} to the ApplicationDbContext class and you will get data access. They don't need to be related. If you need a sample let me know. Hope this helps.

Entity Framework Core not loading related data

We are developing a new application using ASP.NET Core and EF Core. We're on the latest stable release (v1.1.2). We are unable to load related data via navigation properties.
I am aware that lazy loading is not supported in EF Core but every post on the subject I have looked at suggests that we should be able to explicitly load related data using .Include(). However, this is not working for us and the related entities are always null when we load them in code.
We have two entities - 'Exchange' and 'Trade'. 'Exchange' has a foreign key to 'Trade' and contains a Virtual Trade called Request and another called Offer, thus:-
[Table("Exchange")]
public partial class Exchange : BaseEntity
{
public string Pending { get; set; }
[Display(Name = "Exchange Date"), DataType(DataType.Date)]
public DateTime DateOfExchange { get; set; }
public decimal EstimatedHours { get; set; }
public decimal ActualHours { get; set; }
public string Description { get; set; }
public string FollowUp { get; set; }
public string Status { get; set; }
[ForeignKey("User")]
[Required]
public int Broker_Fk { get; set; }
public virtual User Broker { get; set; }
public int Request_Fk { get; set; }
public virtual Trade Request { get; set; }
public int Offer_Fk { get; set; }
public virtual Trade Offer { get; set; }
I have a View Model that instantiates an 'Exchange' which I know has a related 'Request':-
_vm.Exchanges = _context.Exchange.Include(i => i.Request).Where(t => t.Request.User_Fk == user.Id || t.Offer.User_Fk == user.Id).ToList();
This returns an Exchange, which I am passing to and rendering in the View Model:-
#foreach (var item in Model.Exchanges)
{
<span>#item.Request.Name</span> <br />
}
The problem is that #item.Request is null, even though I have explicitly included it when loading the Exchange. I know that there really is a related entity in existence because one of the other properties on Exchange is its foreign key, which is populated.
What am I missing? Every example I have seen posted suggests that what I've done should work.
Your model attributes are messed up:
[Table("Exchange")]
public partial class Exchange : BaseEntity
{
//...
[ForeignKey("Broker")]
[Required]
public int Broker_Fk { get; set; }
public virtual User Broker { get; set; }
[ForeignKey("Request")]
public int Request_Fk { get; set; }
public virtual Trade Request { get; set; }
//...
}

EF 5 Code First using Inheritence in the class

I am getting Error when trying to run this code.
Unable to determine the principal end of an association between the
types 'AddressBook.DAL.Models.User' and 'AddressBook.DAL.Models.User'.
The principal end of this association must be explicitly configured
using either the relationship fluent API or data annotations.
The objective is that i am creating baseClass that has commonfield for all the tables.
IF i don't use base class everything works fine.
namespace AddressBook.DAL.Models
{
public class BaseTable
{
[Required]
public DateTime DateCreated { get; set; }
[Required]
public DateTime DateLastUpdatedOn { get; set; }
[Required]
public virtual int CreatedByUserId { get; set; }
[ForeignKey("CreatedByUserId")]
public virtual User CreatedByUser { get; set; }
[Required]
public virtual int UpdatedByUserId { get; set; }
[ForeignKey("UpdatedByUserId")]
public virtual User UpdatedByUser { get; set; }
[Required]
public RowStatus RowStatus { get; set; }
}
public enum RowStatus
{
NewlyCreated,
Modified,
Deleted
}
}
namespace AddressBook.DAL.Models
{
public class User : BaseTable
{
[Key]
public int UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleName { get; set; }
public string Password { get; set; }
}
}
You need to provide mapping information to EF. The following article describes code-first strategies for different EF entity inheritance models (table-per-type, table-per-hierarchy, etc.). Not all the scenarios are directly what you are doing here, but pay attention to the mapping code because that's what you need to consider (and it's good info in case you want to use inheritance for other scenarios). Note that inheritance does have limitations and costs when it comes to ORMs, particularly with polymorphic associations (which makes the TPC scenario somewhat difficult to manage). http://weblogs.asp.net/manavi/archive/2010/12/24/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph.aspx
The other way EF can handle this kind of scenario is by aggregating a complex type into a "fake" compositional relationship. In other words, even though your audit fields are part of some transactional entity table, you can split them out into a common complex type which can be associated to any other entity that contains those same fields. The difference here is that you'd actually be encapsulting those fields into another type. So for example, if you moved your audit fields into an "Audit" complext type, you would have something like:
User.Audit.DateCreated
instead of
User.DateCreated
In any case, you still need to provide the appropriate mapping information.
This article here explains how to do this: http://weblogs.asp.net/manavi/archive/2010/12/11/entity-association-mapping-with-code-first-part-1-one-to-one-associations.aspx