I am working on an ASP.Net MVC Website.
I have a table called animal, which is created based of this class using Entity Framework code first:
public class Animal
{
public int AnimalId { get; set; }
public string AnimalName { get; set; }
public int NoOfLegs { get; set; }
public int FlyingSpeed { get; set; }
public int RunningSpeed { get; set; }
}
Now I have my AnimalRepository that reads this info from the DB and pass it to my ViewModel (My Domain Classes are different from my ViewModel and I am using ModelReader, ModelWriter and AutoMapper to Map my domain model into my view model as explained here). I have this generic interface in my ViewModel:
public interface IAnimalModel
{
int AnimalId { get; set; }
string AnimalName { get; set; }
int NoOfLegs { get; set; }
}
And I have these two classes in my ViewModel:
public class DogModel: IAnimalModel
{
public int AnimalId { get; set; }
public string AnimalName { get; set; }
public NoOfLegs { get; set; }
public int RunningSpeed { get; set; }
}
public class EagleModel: IAnimalModel
{
public int AnimalId { get; set; }
public string AnimalName { get; set; }
public NoOfLegs { get; set; }
public int FlyingSpeed { get; set; }
}
Now I have one Animal Controller that uses AnimalRepositoy to get the data from DB, and maps it to the correct ViewModel.
I want to bind my View to IAnimalModel interface so that I can pass different animals to the same View.
In my View, I want to use Razor Code to display certain properties based on Animal Model type, something like this:
#if (typeof(Model) == typeof(EagleModel)) {
Html.EditorFor(model => model.FlyingSpeed)
}
#if (typeof(Model) == typeof(DogModel)) {
Html.EditorFor(model => model.RunningSpeed)
}
I have been thinking about this for a long time and I am not sure if this is a good solution? I have quite a few different Animal types with a lot of common properties, so don't really like the idea of creating one Table for each different animal.
I thought it's better to map them to correct type in my ViewModel...
I am not really sure if binding my ViewModel to IAnimalModel interface is a good idea? As I need to check the model type before displaying certain properties.
Another disadvantage is that in my DB, I don't know which type of animal each row contains... I am thinking maybe of I have to add a ViewModelType column to my Animal table, but again I am not sure if this is a good solution?
I think the problem is much simpler to be honest.
You're talking about a lot of animals but you could think in terms of types and then everything simplifies.
So based on some very quick research, you have 6 animal types:
Invertebrates
Fish
Amphibians
Reptiles
Birds
Mammals
You can probably get away with less. So if you look at your problem from this point of view, now you don't have that many different types to look at.
So, have an animal type enum and use that to differentiate the fields you are displaying.
In terms of database, I would not go with one table and null fields as that complicates everything. Either create a base one with whatever is common and a separate one for each type, or go with a Nosql db and save one type data per row.
Finally I don't really think that using a base interface gives you anything. You still have to repeat the fields in every class that implements it and you're not achieving anything in terms of simplification. If you instead go with a base abstract class then at least you don't have to repeat the same properties everywhere and your classes are now smaller and reflect the differences properly.
Related
I'm working on a ASP .NET Core 5 MVC application with MS-SQL database and EF Core is my ORM (with the code-first approach).
Currently I'm redesigning the database, I have multiple tables that share a lot of the same properties, and I want to be able to add/remove properties from different tables, but only in one place in the code.
Because .NET doesn't have multiple class inheritance, I came up with the idea to inherit the classes in "levels".
Basically, in the example below, the purchases table and the products table should have exactly the same prices, dates and content properies, but also some additional specific fields:
class Purchase : PriceDatesAndContent
{
// specific purchase properties
}
class Product : PriceDatesAndContent
{
// specific product properties
}
class PricesDatesAndContent : PriceAndDates
{
public string Name { get; set ; }
public string Description { get; set; }
}
class PricesAndDates : Prices
{
public DateTime someDate1 { get; set; }
public DateTime someDate2 { get; set; }
// ...
}
class Prices
{
public double Price1 { get; set; }
public double Price2 { get; set; }
}
However, I'm not really sure that this is really a brilliant idea as it seems to me, I would love to hear your opinion, or maybe you even have another workaround for this scenario?
Thank you very much!
However, I'm not really sure that this is really a brilliant idea as it seems to me
Having a deep inheritance hierarchy is fine so long as your base classes aren't mapped as Entities. But it is unusual to model your classes this way just to save yourself a bit of typing.
It would probably be better to use interfaces to model the common property patterns, so you don't have to arrange them in a single hierarchy. eg
public interface IHasName
{
public string Name { get; set; }
public string Description { get; set; }
}
public interface IHasPrices
{
public double Price1 { get; set; }
public double Price2 { get; set; }
}
I've googled some around the internet and found some articles about the subject, but none of them satisfied me. I want to know is it good to use object-object mapper to map objects to each other? I know it depends on situation to use, but how will I realize a good or best situation to use?
Taking a step back, it's best practice to separate data transfer objects (DTOs) and view models (VMs) from business objects (entities and alike). Mapping libraries, in that regard, are a means to an end and simply make that association easier.
As far as when, that's up to you. If you feel like you can convert between your business models and DTO/VMs in a way that's easy to maintain, go ahead. From my personal experience, that only goes so far (especially as the requirements change). Therefore, I'm fond of mapping libraries (specifically AutoMapper as I've come to know it's API and am comfortable plugging it in).
Having said that, any time I have to go between these two models I use AutoMapper. I simply configure it once and I'm off and running. Additional tweaks to the models (on either side) then become easier as I can change those bindings in one place (map definition file) and methods automatically "catch up".
Example:
My database contains a Record for a product:
class Product
{
public int Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int QuantityOnHand { get; set; }
public int? ReorderQuantity { get; set; }
public string Sku { get; set; }
}
I may present this to the UI in a more distilled format:
public class ProductViewModel
{
public int Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int Quantity { get; set; }
}
If this came from a repository of some kind, I'm simply calling:
var model = Mapper.Map<ProductViewModel>(productFromRepository)
Here I consistently get the view model I care about from the Product I've requested. If the business/service layer were to add/change/remove properties, I'd only go back to my Mapper.CreateMap<Product, ProductViewModel>() defintion, but the rest of my presentation logic would remain in-tact.
In addition to #Brad Christie's answer, automapping types which have minor differences into a single overarching type is generally easier if you are meaning to display them on your view alongside other products that are generated different ways.
If you'll allow me to crib off one my own previous answers, here's an example:
class SingleProduct {
string Name {get;set;}
decimal Price {get;set;}
decimal GetActualPrice() { return Price; }
}
class ComboSaleProduct {
string Name {get;set;}
List<SingleProduct> ComboProducts {get;set;}
decimal GetActualPrice() { return ComboProducts.Sum(p => p.GetActualPrice()); }
}
class ProductViewModel {
string Name {get;set;}
decimal ActualPrice {get;set;}
}
Automapper wires everything together so that you can return either of these and it will automatically map the "GetActualPrice" to ActualPrice on your viewmodel.
I have 2 models like this.
public partial class Question
{
public int QuestionId { get; set; }
public string QuestionText { get; set; }
public string Ans1 { get; set; }
public string Ans2 { get; set; }
public string Ans3 { get; set; }
public string Ans4 { get; set; }
}
public partial class UserAnswer
{
public int UserAnsId { get; set; }
public Nullable<int> QuestionId { get; set; }
public Nullable<int> UserId { get; set; }
public Nullable<int> AnsVal { get; set; }
}
As you can see QuestionId is in both the models. How can I render it in view. There are multiple questions. Question Moldel has data in initial run but UserAnswer doesn't.
How can I combine these 2 models so that I can use it as IEnumerable in view. Ans1,Ans2,Ans3,Ans4 has text and AnsVal in UserAnswer will get its value from Raiobutton.
make a combine class like below..i am not sure this is perfect or not..any suggestions are acceptable.
public class QuestionAnswerViewModel
{
public Question Question {get;set;}
public ICollection<UserAnswer> Answers {get;set;}
}
You want to create a ViewModel that represents the combined model objects. This keeps things clean, your model is just that, the model, what gets passed to the view can be the model but in many cases the concept of a ViewModel will make things easier to design while keeping your code loosely coupled and clean. This also keeps things that are not important to the View out of the equation aka particular properties in your model such as maybe a CreatedDate should not be passed to the View, especially since View requests will pass back the value as null since it is not being used in the view and thus not populated on postback. This could lead to you updating the database with a null value for CreatedDate simply because it was not used in the View.
Maybe you have a Model class library in your solution? If you do, create another class library called MyNamespace.Web.ViewModels or something like that. Also you should look into using a tool like AutoMapper that will populate the ViewModel on View request to the Controller and populate the model on View postback to the controller.
Say I have a simple model like these (small part of a pretty large app)
public class EHR : IEntity
{
public int ID { get; set; }
public string UserName { get; set; }
public DateTime CreationDate { get; set; }
public virtual ICollection<PhysicalTest> PhysicalTests { get; set; }
}
public class PhysicalTest : IEntity
{
public int ID { get; set; }
public virtual EHR Ehr { get; set; }
public Boolean IsDeleted { get; set; }
}
And i want for an easy way to get the physicalTests that are NOT deleted for a given EHR.
So, I can think of three ways of doing this.
one is simply adding a method to my EHR class.(it doesnt seem as such a bad idea cause I dont want to suffer from anemic domain model)
public IEnumerable<PhysicalTest> ActivePhysicalTests()
{
return this.PhysicalTests.Where(!m=>m.IsDeleted).ToList();
}
the other one is creating an extension method under a EHRRepositoryExtensions class:
public static class EHRRepositoryExtensions
{
public static IEnumerable<PhysicalTest> Active(this IEnumerable<PhysicalTest> physicalTests)
{
return physicalTests.Where(test => !test.IsDeleted).OrderByDescending(test => test.CreationDate).ToList();
}
}
I also think I could have extended my IRepository to include a method that returns only the physsicalTests that arent deleted.
something like
public class EHRRepository : IRepository<EHR>
{
//TODO: method that returns only the physsicalTests that arent deleted.
}
I am still trying to grasp many concepts on DDD and I want it to be as pure as possible.
Which of this approaches would you recommend?
whats a rule of thumb on topics like this?
Please Help.
The first approach is recommended as EHR is your Aggregate Root and it is the information expert about its physical tests.
Second approach is not relevant as you have already the model and you can add this method to the entity instead.
The third approach would be preferable only if the list of physical tests takes much time to load from the database, still you can utilize lazy loading but if you want to separate the fetching from the domain or you dont use a lazy loading enabled ORM then put it as a query method in the repository
Here's a sample of how it works now:
[MetadataType(typeof(PersonMetadata))]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public class PersonMetadata
{
[Required]
public string Name { get; set; }
[Range(0,150]
public int Age { get; set; }
}
However I don't want the MetadataAttribute to be put on the Person class, instead I want some way of telling the MVC framework "hey if you encounter Person class the metadate is in the PersonMetadata class".
It's just reversing the direction which for me feels more compositional and following the Open-Close Principle since I don't have to touch the Person class to "extend" it with metadata.
I know this creates a problem of possible multiple and conflicting metadatas being attached, also increases complexity but flexibility always comes at a price.
I want either a programmatic or declarative way or preferably both :)
So one could be something like :
MetadataTypeMappings.Add(typeof(Person), typeof(PersonMetadata));
Another could be:
[MetadataFor(typeof(Person)]
public class PersonMetadata
{
[Required]
public string Name { get; set; }
[Range(0,150]
public int Age { get; set; }
}
I'm using MvcExtensions. One of the features is a nice way to describe metadata like this:
public class ProductEditModelConfiguration :
ModelMetadataConfiguration<ProductEditModel>
{
public ProductEditModelConfiguration()
{
Configure(model => model.Id).Hide();
Configure(model => model.Name)
.DisplayName(() => LocalizedTexts.Name)
.Required(() => LocalizedTexts.NameCannotBeBlank)
.MaximumLength(64, () =>
LocalizedTexts.NameCannotBeMoreThanSixtyFourCharacters);
}
}
Neat thing is - this allows to use resources for localization in strongly typed fashion.
Drawback for using this - there's bunch of other things bundled which You might not want to use.