Nhibernate Domain Ojbect/View Model Mapping (One-to-Many) - asp.net-mvc

Assume this simple Domain in my core assembly:
public class Country
{
protected ICollection<Province> _provinces = null;
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
public virtual string IsoCode2 { get; set; }
public virtual string IsoCode3 { get; set; }
public virtual int IsoCodeNumeric { get; set; }
public virtual ICollection<Province> Provinces
{
get { return _provinces ?? (_provinces = new List<Province>()); }
set { _provinces = value; }
}
}
public class Province
{
public virtual int Id { get; protected set; }
public virtual Country Country { get; set; }
public virtual string Name { get; set; }
public virtual string Abbreviation { get; set; }
}
The view models in my presentation layer are almost the same:
public class CountryModel
{
public int Id { get; set; }
public string Name { get; set; }
public string IsoCode2 { get; set; }
public string IsoCode3 { get; set; }
public int IsoCodeNumeric { get; set; }
public int NumberOfProvinces { get; set; }
}
public class ProvinceModel
{
public int Id { get; set; }
public int CountryId { get; set; }
public string Name { get; set; }
public string Abbreviation { get; set; }
}
I am creating some Extension methods for mapping back and forth between domain objects/view models:
public static class Extensions
{
public static Country ToEntity(this CountryModel model, Country entity = null)
{
if (entity == null)
entity = new Country();
entity.Name = model.Name;
entity.IsoCode2 = model.IsoCode2;
entity.IsoCode3 = model.IsoCode3;
entity.IsoCodeNumeric = model.IsoCodeNumeric;
entity.AddressFormat = model.AddressFormat;
entity.CanBillTo = model.CanBillTo;
entity.CanShipTo = model.CanShipTo;
entity.IsPublished = model.IsPublished;
return entity;
}
public static CountryModel ToModel(this Country entity, bool includeProvinceCount = false, CountryModel model = null)
{
if (model == null)
model = new CountryModel();
model.Id = entity.Id;
model.Name = entity.Name;
model.IsoCode2 = entity.IsoCode2;
model.IsoCode3 = entity.IsoCode3;
model.IsoCodeNumeric = entity.IsoCodeNumeric;
model.AddressFormat = entity.AddressFormat;
model.CanBillTo = entity.CanBillTo;
model.CanShipTo = entity.CanShipTo;
model.IsPublished = entity.IsPublished;
if (includeProvinceCount)
model.NumberOfProvinces = entity.Provinces.Count;
return model;
}
public static Province ToEntity(this ProvinceModel model, Province entity = null)
{
if (entity == null)
entity = new Province();
//entity.Country = LoadCountryById(model.CountryId); ???? <-- HERE
entity.Name = model.Name;
entity.Abbreviation = model.Abbreviation;
entity.CanBillTo = model.CanBillTo;
entity.CanShipTo = model.CanShipTo;
entity.IsPublished = model.IsPublished;
return entity;
}
public static ProvinceModel ToModel(this Province entity, ProvinceModel model)
{
if (model == null)
model = new ProvinceModel();
model.Id = entity.Id;
model.CountryId = entity.Country.Id;
model.Name = entity.Name;
model.Abbreviation = entity.Abbreviation;
model.CanBillTo = entity.CanBillTo;
model.CanShipTo = entity.CanShipTo;
model.IsPublished = entity.IsPublished;
return model;
}
}
With Entity Framework, the Province domain object would have had both Country and the corresponding CountryId properties. I could assign the Country by simply setting the CountryId.
With NHibernate, the id of the foreign key is unnecessary when creating the domain. So how do you map the ProvinceModel CountryId back to a Country object?
I've gone through all kinds of steps to abstract things into interfaces and use Dependency Injection. Should I use a service locator from within the mapping extensions and look it up? Should I look up the country outside of the mapping extension and require it as a parameter on the extension method? What are the recommended ways of handing this scenario?
Second, with NHibernate they recommend adding helper functions to the domain objects in order to maintain associations (not positive, but I think EF handles this "automagically" for me). For example, I would add a SetCountry method on Province, and AddProvince and RemoveProvince methods on Country.
Doesn't this hurt performance? Instead of simply setting the Country for a Province (which is where the association is managed), the entire list of the new Country's Provinces are loaded to see if it is already in the list before adding to the collection, then the entire list of the old Country's Provinces are loaded to see if the province needs to be removed from the collection.

[in EF] I could assign the Country by simply setting the CountryId.
This isn't true and in my opinion this is a major defect with Entity Framework. Having both Country and CountryId properties is a hack that allows you to set the Country without retrieving it from the database by setting the CountryId. In a web app this works because the record is saved with the CountryId foreign key set so the next time you load it the Country is populated. NHibernate's solution to this pattern is the ISession.Load method that creates a dynamic proxy.
In your example you would do something like
province.Country = session.Load<Country>(provinceModel.CountryId);
As to your second question, in general I only use methods to encapsulate access to collections. This ensures that the collection itself is not replaced by a setter and allows me to maintain both sides of the relationship. I would model this as:
public class Country
{
private ICollection<Province> _provinces;
public Country()
{
_provinces = new HashSet<Province>();
}
public virtual IEnumerable<Province> Provinces
{
get { return _provinces; }
}
public virtual void AddProvince(Province province)
{
province.Country = this;
_provinces.Add(province);
}
public virtual void RemoveProvince(Province province)
{
province.Country = null;
_provinces.Remove(province);
}
}
As you noted, this does require loading the collection. You have to remember that NHibernate (and Hibernate) were originally designed for stateful applications and many of the usage patterns are not strictly necessary in stateless web applications. However, I would profile performance before deviating from some of these patterns. For example, you may want to validate your objects before committing them and that requires that the in-memory representations are consistent.

Related

Null Object Pattern with repository pattern and unit of work

I am using a unit of work to retrieve records / record form database, i am trying to implement some kind of a null object design pattern so that i dont have to check every-time if the returned object is null or not. I have tried searching online however i have not land on any good explanation on how to best achieve this in this current situation, I am familiar with the traditional approach for Null Object Design Pattern where you create a copy null class with hard coded properties and methods and return either the class or null-class based on outcome of the search in Db. however I feel that with the unit of work and repository patterns this approach is not valid. here is the class.
public class HR_Setup_Location
{
public string Street1 { get; set; }
public string Street2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string FullAddress{
get { return $"{Street1} {City} {Country}"; }
}
[ForeignKey("Setup")]
public int SetupId { get; set; }
public virtual Setup Setup { get; set; }
public virtual ICollection<HR_Setup_OfficeEvent> HR_Setup_OfficeEvents { get; set; }
}
I tried the following , which is doing the job for now, however i appreciate your feedback on the approach if you have tried something similar in a similar situation. and what is the best way to address null objects in this pattern.
public interface ISetupLocationRepository : IRepository<HR_Setup_Location>
{
HR_Setup_Location GetById(int LocationId);
}
public class SetupLocationRepository : Repository<HR_Setup_Location>, ISetupLocationRepository
{
private readonly DataBaseContext context;
public SetupLocationRepository(DataBaseContext context)
: base(context)
{
this.context = context;
}
public HR_Setup_Location GetById(int LocationId)
{
HR_Setup_Location Obj = context.HR_Setup_Locations.Where(p => p.HR_Setup_LocationId == LocationId).FirstOrDefault();
if (Obj != null)
{
return Obj;
}
else
{
HR_Setup_Location Obj2 = new HR_Setup_Location()
{
HR_Setup_LocationId = -1,
Street1 = string.Empty,
Street2 = string.Empty,
City = string.Empty,
State = string.Empty,
Country = string.Empty,
SetupId = -1,
};
Obj2.HR_Setup_OfficeEvents = null;
return Obj2;
}
}
}
Then with the unit of work I am trying to access the location address by calling:
string LocationName = Vacancy.HR_Setup_LocationId.HasValue ? unitOfWork.SetupLocations.GetById(Vacancy.HR_Setup_LocationId.Value).FullAddress : "";
so basically if no id is based it will return an empty string, and if an id is passed but the record is no longer available in DataBase then the null object return empty for Fulladdress

Saving many to many relationship tables in Asp.Net MVC

I use Asp.Net MVC, Entity Framework. I have a form it looks like below.
Here, dropdownlist is filled from a table(types). Checkboxes is filled from another table(test). Tables are like below:
public class Types
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
}
public class Tests
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
}
public class Types_Tests
{
public int Types_TestsID{ get; set; }
public int TypesID { get; set; }
public int TestsID { get; set; }
public virtual Types Types { get; set; }
public virtual Tests Tests { get; set; }
}
Types_test table is relation table between Types and Tests. When I click Kaydet button, it shuld save type and checked tests. I made this operation using ViewBag, javascript and hdnvalue.I added checked checkboz values to a hdntext. I made saving process like below:
[HttpPost]
public ActionResult Index(string drpType, string hdntesttypes)
{
var TypeList = Types.GetAll();
ViewBag.TypesList = new SelectList(TypeList, "Id", "Name");
var testypeList = testTypes.GetAll();
ViewBag.TestTypesList = new SelectList(testypeList, "Id", "TestName");
GenericRepository<TestDisabledTypes> testDisabledRepository = new GenericRepository<TestDisabledTypes>(_context);
if (!string.IsNullOrEmpty(hdntesttypes))
{
string[] disabletypesArray = hdntesttypes.Split(',');
using (TransactionScope trns = new TransactionScope())
{
for (int i = 0; i < disabletypesArray.Length; i++)
{
Test_Types types = new Test_Types ();
types.TestTypesID = Convert.ToInt32(disabletypesArray[i]);
types.TypesID = Convert.ToInt32(drpType);
testDisabledRepository.Insert(types);
}
trns.Complete();
}
}
return View();
}
It wokrs. But I search better solution for this process. Can someone give me any idea?
Thanks.
If you don't need additional attributes for your entity class, you don't need create link table.
Just define the following class, and EF will generate the link table for you automatically.
public class Type
{
public int TypesID{get;set;}
public string TestName { get; set; }
public string TestExplanation { get; set; }
public int TestTime { get; set; }
public ICollection<Test> Tests { get; set; }
}
public class Test
{
public int TestID{get;set;
public string Name { get; set; }
public string Code { get; set; }
public ICollection<Type> Types {get;set;}
}
Well, in EntityFramework if you want to create a many to many relation object you need to create new object of "linking" entity. Unfortunately, it is not possible to add first object, add second object and say "Guys, you are in many to many relationships. Are you happy then?" :) You need to create relation object, set appropriate fields in it (I think these are ids of two objects itself) and add it to relation collection (entity) in your model. But before doing so you need to be sure that objects with data you are linking with are already exists in database. Otherwise you'll get an error
Also it's not necessary to create manually transaction because EF does it for you automatically each time you get/save your data

Partial Updates for Entities with Repository/DTO patterns in MVC (prepping for API)

I've built my Domain model layer, my repository layer, and now I'm working on my DTO layer to be used by a webApi project. I'm in the middle of implementing an Update service method, and I'm wondering about partial updates. Here's my DTO class:
public class FullPersonDto
{
public FullPersonDto()
{
Friends = new List<Person>();
}
public FullPersonDto(Person person)
{
PersonId = person.PersonId;
DateCreated = person.DateCreated;
Details = person.Details;
Friends = new List<Person>();
foreach (Person friend in person.Friends)
{
Friends.Add(new PersonDto(friend));
}
}
[Key]
public int PersonId { get; set; }
[Required]
public virtual DateTime DateCreated { get; set; }
public virtual string Details { get; set; }
public List<Person> Friends { get; set; }
public Person ToEntity()
{
var person = new Person
{
PersonId = PersonId,
DateCreated = (DateTime) DateCreated,
Details = Details,
Friends = new List<Person>()
};
foreach (PersonDto friend in Friends)
{
person.Friends.Add(friend.ToEntity());
}
return person;
}
}
Here's my Update method in my Repository:
public Person UpdatePerson(Person person)
{
var entry = _db.Entry(person);
if (entry.State == EntityState.Detached)
{
var dbSet = _db.Set<Person>();
Person attachedPerson = dbSet.Find(person.PersonId);
if (attachedPerson != null)
{
var attachedEntry = _db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person); // what if values are null, like ID, or DateCreated?
}
else
{
entry.State = EntityState.Modified;
}
}
SaveChanges();
return person;
}
My question is: What if I only need to update the Details of a person via my webAPI? Is the convention to construct an entire PersonDto and Update the entire object using SetValues, or is there any way I can specify that I only want a single field updated so that I don't have to send a ton of data over the wire (that I don't really need)?
If it is possible to do partial updates, when is it ever good to update the entire entity? Even if I have to update 5/7 properties, it requires that I send old data for 2/7 to re-write so that SetValues doesn't write nulls into my fields from my DTO.
Any help here would be awesome... totally new to this stuff and trying to learn everything right. Thank you.
I've taken similar approach to do optimization, and I've faced same issues with null values when attaching (not just null, you'll have issue with boolean as well). This is what I've come up with:
public static void Update<T>(this DbContext context, IDTO dto)
where T : class, IEntity
{
T TEntity = context.Set<T>().Local.FirstOrDefault(x => x.Id == dto.Id);
if (TEntity == null)
{
TEntity = context.Set<T>().Create();
TEntity.Id = dto.Id;
context.Set<T>().Attach(TEntity);
}
context.Entry(TEntity).CurrentValues.SetValues(dto);
var attribute = dto.GetAttribute<EnsureUpdatedAttribute>();
if (attribute != null)
{
foreach (var property in attribute.Properties)
context.Entry(TEntity).Property(property).IsModified = true;
}
}
That is extension method for DbContext. Here are the interfaces IDTO and IEntity:
public interface IDTO
{
int Id { get; set; }
}
public interface IEntity
{
int Id { get; set; }
Nullable<DateTime> Modified { get; set; }
Nullable<DateTime> Created { get; set; }
}
I'm using my custom EnsureUpdatedAttribute to annotate what properties should always be updated (to deal with nulls / default values not being tracked):
public class EnsureUpdatedAttribute : Attribute
{
public IEnumerable<string> Properties { get; private set; }
public EnsureUpdatedAttribute(params string[] properties)
{
Properties = properties.AsEnumerable();
}
}
And this is a sample of usage:
public class Sample : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Nullable<DateTime> Modified { get; set; }
public Nullable<DateTime> Created { get; set; }
}
[EnsureUpdated("Active")] /// requirement for entity framework change tracking, read about stub entities
public class SampleDTO : IDTO
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[JsonIgnore] /// How to exclude property from going on the wire / ignored for serialization
public bool Active { get; set; }
}
[HttpPost]
public HttpResponseMessage SaveSample(SampleDTO dto)
{
dto.Active = true;
_ctx.AddModel<Sample>(dto);
_ctx.SaveChanges();
return NoContent();
}
return NoContent() is just extension for returning 204 (NoContent).
Hope this helps.
Theres a few options you have, you can create a stored procedure to update the required parts (I wouldnt do this), or you can manually select the fileds to update on the model before saving the context changes with EF.
Heres an example how to update a specific field:
public void UpdatePerson(int personId, string details)
{
var person = new Person() { Id = personId, Details = details };
db.Persons.Attach(personId);
db.Entry(person).Property(x => x.Details).IsModified = true;
db.SaveChanges();
}
It will depend on your scenario what you want to do, but generally speaking its fine to send your whole entity to be updated, and this is how i would approach your situation potentially changing in the future if needed.

MVC model duplication

I'm very new to ASP.NET MVC, so forgive me if this is something I should know. I haven't seen any obvious documentation on it, so here goes:
I have a LINQ to Entities data model and a MVC project. I use a lot of javascript/jquery, so have opted to access my data from the client through a WebAPI as json objects. However, I don't want to pass all the entity object properties though to the client, so I have added separate models to my MVC project in which I handle MVC model validation and Binding to my Views. Also, in order to work with it in my jquery, I have created json versions of the models.
This is only the start of the project and I don't want to start it off on the wrong foot. Having three versions of my models for each entity in my business layer is going to be a nightmare! I am sure that the overall structure of my project is a very common one, but can't see many developers settling for such duplication of code. There must be a better way of implementing it.
Any thoughts? Really appreciate any input.
In answer to your comment above - you can create your javascript viewmodel as a standard js object. I tend to use Knockout.js so it would look like this:
jsController.Resource = function (data) {
self.UserId = ko.observable(data.UserId);
self.FullName = ko.observable(data.Name);
self.RoleName = ko.observable(data.RoleName);
self.RoleId = ko.observable(data.RoleId);
}
and then use an ajax post method to post it to your MVC action
jsController.addToUndertaking = function (resource, isAsync) {
mylog.log("UndertakingId at post = " + jsController.undertakingId);
var action = $.ajax({
type: "POST",
url: "/TeamMember/AddUserToUndertaking",
data: resource,
cache: false,
async: isAsync
});
action.done(function () {
resource.AllocatedToUndertaking(true);
//Do other funky stuff
});
};
Create your MVC action so that it accepts a forms collection as so:
public ActionResult AddUserToUndertaking(FormCollection postedResource)
{
if (Request.IsAjaxRequest() == false)
{
const string msg = "Non ajax request received";
Logger.ErrorFormat(msg);
throw new SecurityException(msg);
}
if (postedResource == null)
{
Logger.Debug("Null resource posted - terminating.");
return new HttpStatusCodeResult(500);
}
var resource = new AllocatedResourceAjaxViewModel(postedResource);
//Do something Funky
return new HttpStatusCodeResult(200);
}
and then you create your MVC viewmodel from the forms collection (i tend to do this by passing in the forms collection as a constructor method to the viewmodel).
public class AllocatedResourceAjaxViewModel
{
public int UserId { get; set; }
public string Name { get; set; }
public string RoleName { get; set; }
public int RoleId { get; set; }
public AllocatedResourceAjaxViewModel()
{}
public AllocatedResourceAjaxViewModel(NameValueCollection formData)
{
UserId = JsFormDataConverter.Int(formData["UserId"]);
Name = Convert.ToString(formData["FullName"]);
RoleName = Convert.ToString(formData["RoleName"]);
RoleId = JsFormDataConverter.Int(formData["RoleId"]);
}
}
As a null int in your javascript VM will lead to a string of 'undefined' being passed you need a converter method to safely extract non strings.
public static class JsFormDataConverter
{
public static bool Boolean(string formValue, bool defaultValue = false)
{
if (formValue.ToLower() == "true") return true;
if (formValue.ToLower() == "false") return false;
return defaultValue;
}
public static int Int(string formValue, int defaultValue = 0)
{
int result;
return int.TryParse(formValue, out result)
? result
: defaultValue;
}
}
and there you go. I am sure you can improve on the above but that will get you going.
The way that I have always worked is that you have your Models e.g. Order & OrderLines which are where you store all your data and get hydrated either directly from the database by SQL or (more usually these days ) by an ORM such as NHibernate or Entity Framework.
You then have ViewModels - these are used to transport the data from your application to the views - either directly ie a strongly typed view bound to say an OrderViewModel or via an action returning a JsonResult.
A OrderViewModel is not a duplication of Order as it is designed to only hold the data that is needed to be presented on the screen (If you have many different views displaying an Order in different ways it could be perfectly acceptable to have many different ViewModels -one for each view containing only the fields needed for each view). ViewModels should also not contain any complex types except other ViewModels. this helps keep accidental data access out of the views (think security and performance).
So Given
public class Order
{
public int Id { get; set; }
public DateTime OrderDate { get; set; }
public User User { get; set; }
public string Description { get; set; }
public List<OrderLine> OrderLines { get; set; }
}
public class OrderLine
{
public int Id { get; set; }
public Order Order { get; set; }
public String Description { get; set; }
public int Quantity { get; set; }
public int Weight { get; set; }
public int Price { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
You could have the two ViewModels
public class OrderViewModel
{
public int ID { get; set; }
public List<OrderLineViewModel> OrderLines { get; set; }
public DateTime OrderDate { get; set; }
public int UserId { get; set; }
public string Description { get; set; }
}
public class OrderLineViewModel
{
public int Id { get; set; }
public int OrderId { get; set; }
public String Description { get; set; }
public int Quantity { get; set; }
public int Weight { get; set; }
public int Price { get; set; }
}
The view models could then be serialized into JSON as needed or marked up with validation attributes etc.

How to generically map a domain model to a presentation model?

I am trying to figure out how to generically map a domain model to a presentation model. For example, given the following simple objects and interfaces ...
// Product
public class Product : IProduct
{
public int ProductID { get; set; }
public string ProductName { get; set; }
}
public interface IProduct
{
int ProductID { get; set; }
string ProductName { get; set; }
}
// ProductPresentationModel
public class ProductPresentationModel : IProductPresentationModel
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public bool DisplayOrHide { get; set; }
}
public interface IProductPresentationModel
{
int ProductID { get; set; }
string ProductName { get; set; }
bool DisplayOrHide { get; set; }
}
I would like to be able to write code like this ...
MapperObject mapper = new MapperObject();
ProductService service = new ProductService();
ProductPresentationModel model = mapper.Map(service.GetProductByID(productID));
... in which the "MapperObject" could automatically figure out which properties map across the two objects and what sort of objects it is mapping using something like reflection, convention-based mapping, etc. So, I could then just as easily try to map objects like UserPresentationModel and User with the same MapperObject.
Is this possible? If so, how?
EDIT: Just for clarity, here is an example of a non-generic MapperObject that I am currently using:
public class ProductMapper
{
public ProductPresentationModel Map(Product product)
{
var presentationModel = new ProductPresentationModel(new ProductModel())
{
ProductID = product.ProductID,
ProductName = product.ProductName,
ProductDescription = product.ProductDescription,
PricePerMonth = product.PricePerMonth,
ProductCategory = product.ProductCategory,
ProductImagePath = product.ProductImagePath,
ProductActive = product.ProductActive
};
return presentationModel;
}
}
I am still trying to work out how to get this to work with List, instead of just a single Product, but that's a different topic :)
I see want you want. You want to map your domain entities (Product) to aome kind of DTO object (ProductPresentationModel) for communication with your clients (GUI, external services etc).
I you have all this functionality you're looking for packed into AutoMapper framework.
You can write like this with AutoMapper:
Mapper.CreateMap();
look at this wiki https://github.com/AutoMapper/AutoMapper/wiki/Flattening
Good luck.
/Best Regards Magnus

Resources