NHibernate and HasMany mapping - asp.net-mvc

i have trivial mapping for two entities: poll and polloption
Poll:
public class PollMap : ClassMap<Poll>
{
public PollMap() {
Id(x => x.Id);
Map(x => x.Content);
HasMany(x => x.PollOptions).Cascade.All();
}
}
PollOption:
public class PollOptionMap : ClassMap<PollOption>
{
public PollOptionMap() {
Id(x => x.Id);
Map(x => x.Content);
References(x => x.Poll);
}
}
in test code im trying to remove the first polloption of poll entity
Test code:
[Transaction]
public ActionResult Add() {
var poll = new Poll() {
Content = "poll",
PollOptions = new List<PollOption>() {
new PollOption(){
Content="PollOption#1"
},
new PollOption(){
Content="PollOption#2"
}
}
};
GetSession.Save(poll);
return Content("Added");
}
[Transaction]
public ActionResult Removed() {
var poll = GetSession.Query<Poll>().FirstOrDefault();
poll.PollOptions.RemoveAt(0);
GetSession.Update(poll);
return Content("Updated");
}
when the remove action fired it not deleting polloption from db instead it set null in my foreign key :(
ps. google not helped

Cascade.All() only deletes the child object if the parent is deleted. If you want the childs to get deleted when they are removed from the collection, you need Cascade.AllDeleteOrphan().
Additional note: You also have to mark one side of your bidirectional association as Inverse(). More info about that here: http://nhibernate.info/doc/nh/en/index.html#collections-bidirectional

Related

OneToOne mapping with NHibernate and Breeze

Is it possible to make that kind of mapping work with breeze and NHibernate:
public class PeopleMap: BaseMapping<People>
{
public PeopleMap()
{
this.Property(x => x.FirstName);
this.Property(x => x.LastName);
}
}
public class PersonMap : JoinedSubclassMapping<Person>
{
public PersonMap()
{
this.Key(p=>p.Column("ID"));
this.Property(x => x.FirstName);
this.Property(x => x.LastName);
this.Property(x => x.InfoId, map =>
{
map.Insert(false);
map.Update(false);
}
);
this.ManyToOne(x => x.Info, map =>
{
map.Cascade(Cascade.All);
map.Column("InfoId");
});
}
public class PersonInfoMap : BaseMapping<PersonInfo>
{
public PersonInfoMap()
{
this.Property(x => x.Extra);
this.OneToOne(x => x.Person, map =>
{
map.Constrained(true);
map.PropertyReference(p => p.Info);
});
}
}
There is a table per subclass inheritance between people and person. The goal is to make a one to one association between person and personinfo. The mapping works fine in NHibernate. The metadata are generated and queries can be done. The only problem is I can't do a save.
var d = breezeService.manager.createEntity('Person',
{
FirstName: 'Laurent',
LastName: 'Nullens'
});
var l = breezeService.manager.createEntity('PersonInfo',
{
Extra: 'First data',
Person: d
});
d.Info = l;
The result is an exception because the Person entity is saved before the PersonInfo(foreign key exception). I saw in the samples a one to one with Order and InternationlOrder but in that sample both entities share the same primary key.
Is it possbile or is there any workaround like in the Order/InternationalOrder sample?
Have you looked at the breezjs NHibernate sample?
Please do ... and then report back if you can't find what you're looking for.

IQueryable to IEnumerable

public IEnumerable<Temp_Order> Get_Temp(string id)
{
//List<Temp_Order> data = new List<Temp_Order>();
IEnumerable<Temp_Order> data = db.Temp_Order
.Join(db.Items,
t_id => t_id.ItemId,
I_id => I_id.ItemId,
(t_id, I_id) => new { t_id.Quantity, I_id.ItemName })
.Where(x => x.ItemName == id);
return data;
}
In this method I want IEnumerable<Temp_Order>. So I will use this in controller and return to the view.
I'm getting this error:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?) E:\WORK\Projects\RMS_Live\RMS_Live\Models\Order.cs
The Join is converting your query to an IEnumerable of an anonymous type. Add a Select on the end to cast is back to Temp_Order:
public IEnumerable<Temp_Order> Get_Temp(string id)
{
//List<Temp_Order> data = new List<Temp_Order>();
IEnumerable<Temp_Order> data = db.Temp_Order
.Join(db.Items, t_id => t_id.ItemId, I_id => I_id.ItemId, (t_id, I_id) => new { t_id.Quantity, I_id.ItemName })
.Where(x => x.ItemName == id)
.Select(a => new Temp_Order
{
ItemName = a.ItemName,
Property2 = a.Property2,
//snip
});
return data;
}
EDIT:
You indicate in the comments that you want all properties from both Temp_Order and Item which means you need another class. You can get away without creating another class, but it's much simpler in the long run. So first make your class, 2 ways spring to mind, you either replicate all the properties you need or just return the 2 objects, I'll use the latter:
public class OrderItem
{
public Temp_Order Temp_Order { get; set; }
public Item Item { get; set; }
}
Now your function becomes this:
public IEnumerable<OrderItem> Get_Temp(string id)
{
IEnumerable<OrderItem> data = db.Temp_Order
.Join(db.Items,
t_id => t_id.ItemId,
I_id => I_id.ItemId,
(t_id, I_id) => new OrderItem
{
Temp_Order = t_id,
Item = I_id
})
.Where(x => x.ItemName == id);
return data;
}
And in your view, make sure you set the model type to IEnumerable<OrderItem> and you can access all the properties like this:
#Model.Temp_Order.ItemName

How to use Automapper to map controller methods to viewmodel properties for a asp.net mvc application?

I am trying to leverage the Automapper for mapping the controller methods to the viewmodel properties in an asp.net mvc project.
I analyzed few articles about Automapper and found that it is wonderful object to object mapper between complex model object and viewmodel object.
I have code in a controller: CustomersController.cs
[Authorize(Roles = "Administrator")]
public ActionResult Index()
{
var user = _userService.GetUser(_profile.UserName);
if (!user.IsActive)
return RedirectToAction("");
var clientGroups = new List<ClientGroup>();
var model = new CustomerGroupsIndexViewModel()
{
CustomerGroupUsersUrl = Url.RouteUrl<CustomerGroupsController>(c => c.GetUsersForCustomerGroup(null, null, null, 0, 0)),
CustomerGroupByAreaUrl = Url.RouteUrl<CustomerGroupsController >(c => c.GetAreaDetailsForCustomerGroup(null, null, null, 0, 0)),
CheckLoginNameUrl = Url.RouteUrl<UsersController>(c => c.CheckLoginName(null)),
ResetUserUrl = Url.RouteUrl<UsersController>(c => c.ResetPassword(null)),
GetSelectOptionsForCustomerGroupUrl = Url.RouteUrl<ClientGroupsController>(c => c.GetSelectOptionsForCustomerGroup(null,null)),
FindUsersMatchingTermUrl = Url.RouteUrl<UsersController>(c => c.FindUsersMatchingWithLoginName(null)),
NumberOfTestTaken = _scanService.GetCustomerForUser(user).Count(),
RefreshCustomerGroupUrl = Url.RouteUrl<CustomerGroupsController >(c => c.RefreshClientGroup()),
};
Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(-1));
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
return View("CustomerGroupIndex", model);
}
I have such methods across the project code base.
Can anyone help me how can I use Automapper efficiently here?
Thanks & Regards,
Santosh Kumar Patro
I 'll explain it very briefly
Create configuration for each Model and related ViewModel like this
Interface for configuration:
interface IGlobalConfiguration
{
void Configure();
}
Class for configuration:
public class AutoMapperViewModelConfiguration : IGlobalConfiguration
{
public void Configure()
{
Mapper.CreateMap<Model1,ViewModel1>();
Mapper.CreateMap<ViewModel1,Model1>()
.ForMember(x => x.ModelMember1, y => y.Ignore());//Ignore if not required
}
}
Create a class and method like this:
public class GlobalConfigurationModule
{
private readonly Assembly assembly;
public GlobalConfigurationModule()
: this(Assembly.GetExecutingAssembly())
{
}
public GlobalConfigurationModule(Assembly assembly)
{
this.assembly = assembly;
}
public void Load()
{
var ins = from x in assembly.GetExportedTypes()
where x.GetInterfaces().Contains(typeof(IGlobalConfiguration))
select Activator.CreateInstance(x) as IGlobalConfiguration;
foreach (var config in ins)
{
config.Configure();
}
}
}
Call the Load method in Global.asax
protected void Application_Start()
{
new GlobalConfigurationModule().Load();
}
When you require to map a Model to ViewModel use this Mapper.Map() method like this:
Mapper.Map(model1Object, viewModel1Object);
Edit:
If you need the mapping should be done from a method you can create mapping like this.
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.SomeValue,
opt => opt.MapFrom(src => src.GetSomeValue()))
You cannot map a method returns void.
Cheers

Kendo Grid model with an IEnumerable property not updating correctly after Create/Update when using AJAX binding

I'm having a problem where a property of my model is not being correctly updated when sending it to my controller for an Update or Create call from a Kendo Grid. The model looks like this:
public class ReleaseNotesModel
{
public int NoteID { get; set; }
public int ReleaseID { get; set; }
public List<TranslationModel> ReleaseNoteTranslations { get; set; }
public ReleaseNoteType ItemType { get; set; }
}
public class TranslationModel
{
public int TranslationID { get; set; }
public string Translation { get; set; }
public int LanguageID { get; set; }
public int ItemID { get; set; }
}
Here is the grid in my view:
#(Html.Kendo().Grid<ReleaseNotesModel>()
.Name("Grid")
.Columns(columns =>
{
columns.Bound(m => m.ItemType).Width(140);
columns.Bound(m => m.Description);
columns.Command(command =>
{
command.Edit();
command.Destroy();
}).Width(170);
})
.ToolBar(toolbar => toolbar.Create())
.Editable(editable => editable
.Mode(GridEditMode.PopUp)
.TemplateName("ReleaseNoteTemplate")
.Window(w => w.Width(620))
.DisplayDeleteConfirmation(true)
)
.Pageable()
.Sortable()
.Scrollable()
.Filterable()
.DataSource(dataSource => dataSource
.Ajax()
.ServerOperation(false)
//.Server()
.Events(e => e.Error("grid_error"))
.Model(model =>
{
model.Id(m => m.NoteID);
model.Field(m => m.ReleaseID).DefaultValue(Model.ReleaseID);
model.Field(m => m.ItemType).DefaultValue(ReleaseNoteType.NewFeature);
//defaultTranslationsList is a List<TranslationModel> with two empty objects in it
model.Field(m => m.ReleaseNoteTranslations).DefaultValue(defaultTranslationsList);
})
.PageSize(5)
.Read(read => read.Action("GetNotes", "ReleaseNotes", new { releaseID = Model.ReleaseID }))
.Create(create => create.Action("AddNote", "ReleaseNotes"))
.Update(update => update.Action("EditNote", "ReleaseNotes"))
.Destroy(destroy => destroy.Action("DeleteNote", "ReleaseNotes"))
)
)
So more specifically, the problem I am having is that in my controller action:
public async Task<ActionResult> EditNote(ReleaseNotesModel model)
model.ReleaseNoteTranslations always contains two empty objects (properties are null or 0), i.e. the default value which I set for this property. If I set no default value, then I won't have any fields to edit for this property in the popup editor. All the other properties are updated as expected.
What bugs me is that if I use server binding instead of AJAX, then all the data is correctly received. So I decided to check out the data in the request headers being sent in both cases:
// Using server binding
ReleaseID:300
NoteID:886
ItemType:1
ReleaseNoteTranslations[0].ItemID:886
ReleaseNoteTranslations[0].LanguageID:1
ReleaseNoteTranslations[0].TranslationID:869
ReleaseNoteTranslations[0].Translation:The module is now released!
ReleaseNoteTranslations[1].ItemID:886
ReleaseNoteTranslations[1].LanguageID:2
ReleaseNoteTranslations[1].TranslationID:870
ReleaseNoteTranslations[1].Translation:Le module est maintenant disponible!
NoteID:886
// Using AJAX binding
sort:
group:
filter:
NoteID:886
ReleaseID:300
ReleaseNoteTranslations[0][TranslationID]:869
ReleaseNoteTranslations[0][Translation]:The module is now released!
ReleaseNoteTranslations[0][LanguageID]:1
ReleaseNoteTranslations[0][ItemID]:886
ReleaseNoteTranslations[1][TranslationID]:870
ReleaseNoteTranslations[1][Translation]:Le module est maintenant disponible!
ReleaseNoteTranslations[1][LanguageID]:2
ReleaseNoteTranslations[1][ItemID]:886
ItemType:1
Now what I notice first here is the syntax of objectName[index].PropertyName vs objectName[index][PropertyName]
I wonder if this could be the cause of my problem, and if so, is there a way for me to go and directly manipulate the data being sent to fix it? Could this be a bug in the way Kendo Grid sends data through Ajax binding?
Either way, any help would be much appreciated!
So In case anyone stumbles on this in the future, I contacted Telerik support, who explained to me that:
The dataSource supports only value types and will not serialize the
arrays in the format that is expected by the model binder.
They also provided me with a workaround using the request Data function to call a JavaScript function which converts the data into the correct format.
In the view, modify the request functions by specifying the name of the JavaScript function to call:
.Create(create => create.Action("AddNote", "ReleaseNotes").Data("serialize"))
And then add in the functions which will do the conversion:
function serialize(data) {
for (var property in data) {
if ($.isArray(data[property])) {
serializeArray(property, data[property], data);
}
}
}
function serializeArray(prefix, array, result) {
for (var i = 0; i < array.length; i++) {
for (var property in array[i]) {
result[prefix + "[" + i + "]." + property] = array[i][property];
}
}
}
Another issue could be that the kendo.aspnet.mvc.js is not included in the project. It appears to do the serialization trick when included.
Something that I noticed is that you need to add if condition as you don't need to serialize if the count is 1, just one item works fine without serialization

Architecture abstraction problem in Repository ASP.NET MVC

I'm working with MVC ASP.NET 3.5 SP1 on VS2008.
I'm looking for a way to abstract this three methods I have in my Users repository.
public User GetUser(Log log)
{
return db.Users.FirstOrDefault(u => u.Logs.Any(l => l.id.Equals(log.id)));
}
public User GetUser(Product product)
{
return db.Users.FirstOrDefault(u => u.Products.Any(pr => pr.id.Equals(product.id)));
}
public User GetUser(Photo photo)
{
return db.Users.FirstOrDefault(u => u.Photos.Any(ph => ph.id.Equals(photo.id)));
}
My DB.edmx contains the models
User [id, username, ...]
Product [id, name, ...]
Photo [id, name, ...]
Log [id, data, ...]
Is it possible to have only ONE method for all of these (and future) methods based upon model.id search?
public User GetUser(Expression<Func<User, bool>> restriction)
{
return db.Users.Where(restriction).FirstOrDefault();
}
Now use it:
var u = Repository.GetUser(u => u.Logs.Any(l => l.id.Equals(log.id)));
You can also use MS DynamicQuery:
using System.Linq.Dynamic;
//...
public User GetUser(string propertyName, int id)
{
var restriction = propertyName + ".Any(id = #0)";
return db.Users.Where(restriction, id).FirstOrDefault();
}
var u = Repository.GetUser("Logs", log.id);
I may not have the syntax quite correct, but you get the idea.
If all the associated entities (Log, Product and Photo) will be searched by a common property (id INT) then maybe you could try something like this...
First, create an interface:
public interface IUserAssociation
{
int id { get; }
}
Then each of the three classes would implement this interface like so:
public partial class Product : IUserAssociation
{
}
The the GetUser method would look like so:
public User GetUser<T>(T entity) where T : IUserAssociation
{
var type = typeof(T);
if (type == typeof(Log))
{
return db.Users.FirstOrDefault(u => u.Logs.Any(l => l.id.Equals(entity.id)));
}
else if (type == typeof(Product))
{
return db.Users.FirstOrDefault(u => u.Products.Any(pr => pr.id.Equals(entity.id)));
}
else if (type == typeof(Photo))
{
return db.Users.FirstOrDefault(u => u.Photos.Any(ph => ph.id.Equals(entity.id)));
}
else
{
throw new ArgumentException();
}
}
Then you should be able to call GetUser and pass it a Log, Photo or Product entity from one method. It is not very elegant but would work for this specific situation.
I like Craig's solution better but I'd suggest this:
Repository.GetUser(u => u.Logs, log);
Which will be possible if all your entities derive from
public interface IEntity { public int Id { get; } }
Then method will be like
public User GetUser<T, Y>(Func<T, IList<Y>> getlist, Y sample)
where T: IEntity
where Y: IEntity
{
return db.Users.Select(x => getlist(x).Any(y => y.Id == sample.Id)).FirstOrDefault();
}
Also if we take idea of S#arp Architecture that if entity1.Id == entity2.Id (for persistent entities) then Equals(entity1, entity2) - we can use getlist(x).Contains(sample).

Resources