How to Query Icollections of Entity Framework Code First Data - asp.net-mvc

I'm following this post here
his code looks like so
from p in ctx.Persons
where p.ID == personId
select new PersonInfo
{
Name = p.FirstName + " " + p.LastName,
BornIn = p.BornInCity.Name,
LivesIn = p.LivesInCity.Name,
Gender = p.Sex.Name,
CarsOwnedCount = p.Cars.Count(),
}
Now when he is using p.LivesInCity.Name ( My Assumption is that he has 2 different ViewModels Classes and 1 of them has an IEnumerable or some sort of collection of City names?)
Question
How can he access p.LivesInCity.Name but when i Try p.Countys.[CountyColectionForSections].[SectionProperty] I get See Image Below
here is how i setup my ViewModels
Table 1
public string Client { get; set; }
public virtual ICollection<dbProspect> Prospects { get; set; }
Table 2
public int Prospect { get; set; }
public virtual ICollection<dbCounty> Countys { get; set; }
public virtual ICollection<UserProfiles> UserProfiles { get; set; }
Table 3...
Table 3 Would have a collection of another table, and table 4 would have another colection and so on Basically a Table Hierarchy about 15 deep.
here is how i try and access them
var x = from c in db.Client
select new ViewModelExcelReport
{
client = c.ClientName,
cntyCounty = p.Countys. //Here is where i would like to say p.Countys.[CountyProperty]
sctSection = p.Countys.[CountyColectionForSections].[SectionProperty]
}

You could do the following if you expect only one County per Client and I'm guessing at the relation between the entities:
var x = from c in db.Client
from p in c.Prospects
from ct in p.Countys
select new ViewModelExcelReport
{
client = c.ClientName,
cntyCounty = ct.County,
sctSection = ct.Section
};

The first example works because he's only referencing a single instance of an entity (LivesIn of type City), and then referencing a single primitive value within (Name property is just a string).
I see you're trying to project a new type by referencing an IEnumerable (Countys), but you never actually enumerate/iterate/materialize an instance.
p.Countys.[CountyProperty] will never exist, because you have a reference to a collection and you must get a reference to a single object first. You can do that by using Countys.Single() or SingleOrDefault() but I doubt that's what you want. I think what you want is to traverse the model in the opposite direction. From the "Many" side to the "One" side. Something like this:
var x = from c in db.County
select new ViewModelExcelReport
{
County = c.Name,
CountyProperty = c.AnyProperty,
Section = c.Section.Name,
Prospect = c.Prospect.Name,
Client = c.Prospect.Client.Name
}

Related

Neo4jClient shows strange behavior passing objects

I'm trying to query data and objects from my Neo4j database using Neo4jClient in ASP.NET MVC5.
I have to structure the returned data in specific objects. This works fine for IEnumerates, but does not work for single objects. I can't see any reason for this behavior as Neo4j itself is returning the results in the expected way.
Here a sample:
public class ProjectContainer
{
public string relationship { get; set; }
public int accesstype { get; set; }
public ProjectData data { get; set; }
}
var query = graphClient.Cypher
.Match("(positionData:Position)<-[relationData:HAS_POSITION]-(projectData:Project)")
.Where((PositionData positionData) => positionData.uuid == uuid)
.With("positionData, projectData, { relationship: type(relationData), accesstype:0, data: projectData } as container ")
.Return((positionData, projectData, container) => new
{
position = positionData.As<PositionData>(),
projectdata = projectData.As<ProjectData>(),
projectcontainer = container.As<ProjectContainer>(),
projects = container.CollectAs<ProjectContainer>()
});
var pos = query.Results.First();
Neo4j return a single JSON-object for container.
pos.projects contains - as expected - an enumeration of 1 element of ProjectContainer including all properties an projectdata in "data". But I don't want an enumeration as it will be always 1 project.
The problems are:
problem: pos.projectscontainer is not null, but only contains null-properties. It should be filled by the same content as projects[0], but this doesn't work.
problem: if I rename the property "data" in "project" (the property itself and in with-clause), even pos.projectcontainer will be null. Why does the name of the property changes the behavior, is "data" any special keyword for the client?
I tested the query from query.Query.DebugQueryText in the Neo4j browser and the result is as expected, even if I name the property "project" or "data":
MATCH (positionData:Position)<-[relationData:HAS_POSITION]-(projectData:Project) WHERE (positionData.uuid = "c3a1eedc-5083-4784-a378-cfd2ba0bec57") WITH positionData, projectData, { relationship: type(relationData), accesstype:0, data: projectData } as container RETURN positionData AS position, projectData AS projectdata, container AS projectcontainer, collect(container) AS projects
╒══════════════════════════════╤══════════════════════════════╤══════════════════════════════╕
│"projectdata" │"projectcontainer" │"projects" │
╞══════════════════════════════╪══════════════════════════════╪══════════════════════════════╡
│{"description":"Project dummy │{"relationship":"HAS_POSITION"│[{"relationship":"HAS_POSITION│
│description","closed":false,"a│,"accesstype":"0","data":{"des│","accesstype":"0","data":{"de│
│ctive":true,"published":false,│cription":"Project dummy descr│scription":"Project dummy desc│
│"title":"Project dummy title",│iption","closed":false,"active│ription","closed":false,"activ│
│"uuid":"e4327251-d0c7-4e24-aa1│":true,"published":false,"titl│e":true,"published":false,"tit│
│2-cf7ade4a512a"} │e":"Project dummy title","uuid│le":"Project dummy title","uui│
│ │":"e4327251-d0c7-4e24-aa12-cf7│d":"e4327251-d0c7-4e24-aa12-cf│
│ │ade4a512a"}} │7ade4a512a"}}] │
└──────────────────────────────┴──────────────────────────────┴──────────────────────────────┘
Thanks for any help.
Reiner
You're right that it is an existing issue - so this isn't a solution per se, but a work-around to get the object filled in.
I don't know of a way to achieve what you want with anonymous types, only with a concrete class type, so if you had a class called Altogether which looked like this:
public class Altogether
{
public PositionData position { get; set; }
public ProjectData projectdata { get; set; }
public ProjectContainer projectcontainer { get; set;}
}
you can then add this line to your current query:
.With("{position: positionData, projectdata: projectData, projectcontainer: container} as altogether")
And return that instead, so the whole query looks like:
var query = graphClient.Cypher
.Match("(positionData:Position)<-[relationData:HAS_POSITION]-(projectData:Project)")
.Where((PositionData positionData) => positionData.uuid == uuid)
.With("positionData, projectData, { relationship: type(relationData), accesstype:0, data: projectData } as container")
.With("{position: positionData, projectdata: projectData, projectcontainer: container} as altogether") //<-- Add here
.Return((altogether) => altogether.As<Altogether>()); //<-- Change here
It's not great, but it will fill your objects properly :/

Custom fields in viewmodel based off of model fields

I'm fairly new to ASP.NET MVC and need some help.
I have a model called "Project", simplified below:
public class Project
{
public int ProjId { get; set; }
public string StatusID { get; set; }
}
The StatusID is a 1 character string, and can be I (In Progress), C (Completed), or K (Killed). So when I get a list of projects from the database, it returns that 1 character status code.
However, when I pass that information to my view, I'd like to show the full description rather than the 1 character.
What's the best way to approach this situation?
Thanks!
The best way would be to create a one-to-many relationship with a Statuses table. Then create a separate view model with this information.
var vm = new ProjectVM
{
ProjId = project.ProjId,
StatusDescription = statuses.Find(project.StatusId).Description
};
Notices statuses is a repository API that finds the right status description.

Only getting one child observable when I retrieve complete objects graph

I have a large object graph that I want to return to the client (an overview of the entire model) so I decided to send it back as one big object (I'm returning it as the object in question.)
In Breeze however I'm only getting the first object in each dependent object. So for example I have a 'policy' object with two 'vehicle' objects. I only see one vehicle (even when I put a breakpoint at var p = data.results[0]. The json coming back from the call shows two vehicles but breeze is catching only one.
What can I do to get the other ones? Here's the call I'm making:
var getPolicyByPolicyNumber = function (lob, policynumber, policyObservable) {
var params = {};
params['lOBCd'] = lob;
params['policyNumber'] = policynumber;
var query = EntityQuery.from('PolicyInquiry')
.withParameters(params);
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
var p = data.results[0];
p.completeTree(true);
return policyObservable(p);
}
};
And in my breeze controller I have the following:
[System.Web.Http.HttpGet]
public Policy PolicyInquiry(string lOBCd, string policyNumber)
{
UserProfile userProfile = _contextProvider.Context.UserProfiles.SingleOrDefault(u => u.UserName == WebSecurity.CurrentUserName);
var policy = Uow.PolicyServices.GetPolicy(userProfile.BrokerId, userProfile.AgencyId, "csio:" + lOBCd, policyNumber);
return policy;
}
And here's an abbreviated model showing policy and vehicle:
public class Policy
{
public int Id { get; set; }
public string PolicyNumber { get; set; }
[InverseProperty("Policy")]
public ICollection<Vehicle> Vehicles { get; set; }
// other fields removed
}
public class Vehicle
{
public int Id {get; set}
public string ItemId { get; set; }
// other fields removed
//Foreign Keys
public int PolicyId { get; set; }
[IgnoreDataMember]
[ForeignKey("PolicyId")]
[Required]
public virtual Policy Policy { get; set; }
}
Now that I see your model I think I see the issue.
Breeze does not automatically resolve the entity graph on a query. i.e. if you retrieve a Policy, Breeze only returns the Policy instance itself without any navigation properties resolved. This is by design, so that a single query doesn't bring down the entire entity graph.
If you want the values of any navigation properties you have three options, the third of which is probably your best bet. I've taken some liberties in simplifying your model for the purposes of explanation. These all assume that the "Policy" type is actually defined as a Breeze entity type, i.e. has a metadata definition in the Breeze metadataStore.
1) Use an client side EntityQuery.expand clause
var query = EntityQuery.from('Policy')
.expand("Vehicles")
.withParameters(params);
2) Use a server side Include clause
[System.Web.Http.HttpGet]
public IEnumberable<Policy>(string lOBCd, string policyNumber) {
return _contextProvider.Context.Policies.Where(....).Include("Vehicles");
}
3) Use a anonymous result, that contains two known entity types.
[System.Web.Http.HttpGet]
public Object PolicyInquiry(string lOBCd, string policyNumber) {
UserProfile userProfile = _contextProvider.Context.UserProfiles.SingleOrDefault(u => u.UserName == WebSecurity.CurrentUserName);
var policy = Uow.PolicyServices.GetPolicy(userProfile.BrokerId, userProfile.AgencyId, "csio:" + lOBCd, policyNumber);
return new {
Policy = policy,
Vehicles = policy.GetVehicles() // insure that they are actually resolved on the server)
}
return policy;
}
More info here: Navigation Properties
I hope this is clear enough.
Sorry, but without seeing the underlying implementation of "policy" and its metadata, it's hard to tell what's going on. But I can make a general suggestion.
1) If you want to return an aggregate object and have Breeze recognize it's constituent parts, the recommended mechanism is to create a projection and return that. i.e. something like
public IQueryable<Object> CompanyInfoAndOrders() {
return ContextProvider.Context.Customers.Select(c => new { Customer = c, Orders = c.Orders });
}
In this example, providing that Breeze has metadata for the Customer and Order types,
Breeze will deconstruct the result and add the Customer and its orders to the EntityManager, and return a collection of json objects each with a "Customer" and Orders property. The Customer and individual Orders will each have been "adapted" to the current model library as well (i.e. knockout, backbone, or backingStore - for Angular).

Want to save selected (i.e., more than 1) enums as string with NHibernate

I cannot for the life of me get this to work with my existing code, but I am trying to save my enum selections as strings in NHibernate. Basically, I have a UI check box and if the user selects multiple check boxes I want to store those selections. Now, I can get NHibernate to store ONE selection (e.g., from a drop down or radio button list, where the user is limited to one choice only).
This is the jist of what I have for an enum:
public enum IncomeType
{
[Display(Name = "Full-Time Employment")]
FullTime,
[Display(Name = "Part-Time Employment")]
PartTime,
[Display(Name = "Self-Employment")]
SelfEmployed,
[Display(Name = "Rental")]
Rental,
[Display(Name = "Social Security Payments")]
SocialSecurity,
[Display(Name = "Retirement / Pension Payments")]
Retirement,
[Display(Name = "Child Support Payments")]
ChildSupport,
[Display(Name = "Spousal Maintenance")]
Maintenance,
[Display(Name = "Other")]
Other
}
I use a method to "select" whether a checkbox list is shown (if my BulkItemThreshold equals the number of options, a checkbox list is displayed). Here is that method:
public static IEnumerable<SelectListItem> GetItemsFromEnumString<T>
(T selectedValue = default(T)) where T : struct
{
return from name in Enum.GetNames(typeof(T))
let enumValue = Convert.ToString((T)Enum.Parse(typeof(T), name, true))
select new SelectListItem
{
Text = GetEnumDescription(name, typeof(T)),
Value = enumValue,
Selected = enumValue.Equals(selectedValue)
};
}
(Note: some items in there are helpers, but I don't believe they are relevant; also, the selected input is displayed using a template .cshtml file - again, not sure if that's relevant)
Now, I call this thusly:
public class IncomeTypeSelectorAttribute : SelectorAttribute
{
public override IEnumerable<SelectListItem> GetItems()
{
return Selector.GetItemsFromEnumString<IncomeType>();
}
}
And finally, we get to the virtual property (using a proxy) but this is where NHibernate throws a wrench (Note: this was working fine for me before NHibernate, and now I am trying to get many lines of code working with it WITHOUT having to re-do everything; if I re-do everything I will probably triple the code I already have to get it to work):
Property (record):
[IncomeTypeSelector(BulkSelectionThreshold = 9)]
public virtual List<string> IndividualIncomeTypeCheckBox { get; set; }
proxy (part):
public List<string> IndividualIncomeTypeCheckBox
{
get { return Record.IndividualIncomeTypeCheckBox; }
set { Record.IndividualIncomeTypeCheckBox = value; }
}
Again, this is how I was doing things and it was working great before NHibernate. But now I have to use NHibernate. No getting around it.
I am using a service class that it tying the two together in a Create method to save in the DB with NHibernate, and for the above it would ordinarily look like this:
part.IndividualIncomeTypeCheckBox = record.IndividualIncomeTypeCheckBox;
This would work if it were just one selection.
Well, I've spent a good two (2) months trying to get this to work. It's tough because I have lots of code where the user can make only one selection (such as with a radiobutton list) and it works GREAT - even with NHibernate. Let me give you an example:
public virtual IncomeType? IndividualIncomeTypeCheckBox { get; set; }
If I do the above, it will display a drop down list, and NHibernate will store the ONE allowable option selected by the user in the DB no problem. But more than one option with List<string> does not work.
Now, I have tried everything I could find here or elsewhere and nothing works. Yes, I know it should be IList<IncomeType> or some other variant. But if I use this then NHibernate requires that IncomeType be another table in the DB. This is too much code to write for such a simple thing I believe. We are not talking a many-to-many relation in the sense that this is not a User with Multiple addresses (wherein addresses would have street, city, state, zip, etc.).
I have tried different types of proxy get and set code, but nothing works. I have tried [Flags] and other things working with string only, but to no avail. Those last solutions would "work" but ONLY to save the first item selected out of multiple (i.e., in my scenario, if the user selected "FullTime" and "Rental" as Income Types, then only "FullTime" (string) would be saved or "1" ([Flags]/int), not both items selected.
I have a situation where I re-display the choices using a ReadOnly attribute like this:
[IncomeTypeSelector]
[ReadOnly(true)]
public List<string> IndividualIncomeTypeCheckBoxPost
{
get { return IndividualIncomeTypeCheckBox; }
}
This would display on the UI, but I tried doing something like this with NHibernate and it wouldn't work.
Could anyone please show me, using the above, how I can go about getting NHibernate to store more than one enum in this checkbox list scenario?
UPDATE:
More poking around here and on the web, and I came up with the following (which still does not work).
Property (record):
[IncomeTypeSelector(BulkSelectionThreshold = 9)]
public virtual IList<IncomeTypeRecord> IndividualIncomeTypeCheckBox
{
get { return incomeType; }
set { incomeType= value; }
}
private IList<IncomeTypeRecord> incomeType =
new List<IncomeTypeRecord>();
Proxy (part):
public IList<IncomeTypeRecord> IndividualIncomeTypeCheckBox
{
get { return Record.IndividualIncomeTypeCheckBox; }
set { Record.IndividualIncomeTypeCheckBox= value; }
}
And a change to the enum:
public enum IncomeType : int // removing int & value still gives validate error
{
[Display(Name = "Full-Time Employment")]
FullTime = 1,
[Display(Name = "Part-Time Employment")]
PartTime,
....
}
And I added this class to support IncomeTypeRecord
public class IncomeTypeRecord
{
public virtual int Id { get; set; }
public virtual IncomeType Value { get; set; }
}
HOWEVER, when I get to the UI screen and pick one or more options I get a validation error (value not valid). For example, say I pick FullTime alone, or pick FullTime and Retirement, then the UI will display the following error:
The value 'FullTime' is invalid.
The value 'FullTime,Retirement' is invalid.
(respectively)
Even if I remove the int declaration for the enum and get rid of the value I started with "1", I still get this validation error. I tried messing around with and adding different model binders (which now has me stumped as to whether my original problem still exists and now I have a different problem - but you still get bounty points :) ).
Pulling my hair out. If I could offer more bounty I would. I need a definitive solution. I appreciate any help.
UPDATE
This is what I have so far:
Record:
public virtual string IndividualIncomeTypeCheckBox{ get; set; }
Part:
//If I do IEnumberable<string> my .Select throws a cast error
public IEnumerable<IncomeType> IndividualIncomeTypeCheckBox
{
get
{
return Record
.IndividualIncomeTypeCheckBox
.Split(',')
.Select(r => (IncomeType)Enum.Parse(typeof(IncomeType), r));
}
set { Record.IndividualIncomeTypeCheckBox= value
== null ? null : String.Join(",", value); }
}
Service Class:
public SimplePart CreateSimple(SimplePartRecord record)
{
SimplePart simple = Services.ContentManager.Create<SimplePart>("Simple");
...
//How I would save a FirstName property (example Part / PartRecord below)
//public virtual string FirstName { get; set; } - PartRecord
//public string FirstName - Part
//{
// get { return Record.FirstName ; }
// set { Record.FirstName= value; }
//}
simple.FirstName = record.FristName;
...
//I obviously cannot do the following with the above IncomeType
//Getting cannot convert string to IEnumerable error
//How would I write this:
simple.IndividualIncomeTypeCheckBox = record.IndividualIncomeTypeCheckBox;
...
}
And this is how it's called in a controller (this persists to DB): (Updating Controller code)
public ActionResult Confirm(string backButton, string nextButton)
{
if (backButton != null)
return RedirectToAction("WrapUp");
else if ((nextButton != null) && ModelState.IsValid)
{
_myService.CreateSimple(myData.SimplePartRecord);
return RedirectToAction("Submitted");
}
else
return View(myData);
}
Updating with additional code (serialization and view model):
"myData" is defined in the controller (using Serialization) as:
private MyViewModel myData;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var serialized = Request.Form["myData"];
if (serialized != null)
{
myData = (MyViewModel)new MvcSerializer().Deserialize
(serialized, SerializationMode.Signed);
TryUpdateModel(myData);
}
else
myData = (MyViewModel)TempData["myData"] ?? new MyViewModel();
TempData.Keep();
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (filterContext.Result is RedirectToRouteResult)
TempData["myData"] = myData;
}
I use Serialization because I set up a multi-step wizard (as seen in the controller action "backButton" "nextButton) on the front-end. I am not using a driver (which can only display Admin or on the front-end but then only on .cshtml files directly under the ~/Views folder (not in a structured folder list like I am using)). No driver = no update view model type code = no mechanism to "create" the data in the DB. If I do not use some "create" type method, the form will submit but all the data will be "NULL".
When you say that the data should be persisted automatically, I am sorry but I do not see how. All the stuff I read or code I review has SOME method of updating the DB with whatever is entered in a form. If I am missing something, my apologies.
"MyViewModel" is pretty straightforward:
[Serializabel]
public class MyViewModel
{
public SimplePartRecord SimplePartRecord { get; set; }
}
And, just in case, here is the relevant portion of the migration (return 1 is a completely separate and unrelated table):
public int UpdateFrom1()
{
SchemaBuilder.CreateTable("SimplePartRecord",
table => table
.ContentPartRecord()
...
.Column("IndividualIncomeTypeCheckBox", DbType.String)
...
);
ContentDefinitionManager.AlterPartDefinition("SimplePart",
part => part
.Attachable(false));
return 2;
}
The error I am getting is
Cannot implicitly convert type 'string' to 'System.Collections.Generic.IEnumerable'"
when I do the following in the "Create" method of my service class:
simple.IndividualIncomeTypeCheckBox = record.IndividualIncomeTypeCheckBox;
One additional thought: I tried using the n-n Relation sample to handle this scenario. Aside from it being a lot of extra code for what I thought should be straightforward and simple, because of the way I am using Serialization I had a lot of object reference errors and could not figure out how to properly code my controller to handle it.
There's a lot of info to wade through here so hopefully I haven't missed the point. It appears to me that the goals are:
The business class has a collection property of IList<IncomeType> without requiring an additional table
The values in that collection should be persisted as a delimited string of the enum names
The best approach is to use a custom user type (an implementation of NHibernate.UserTypes.IUserType) to map the property. Below is a generic IUserType that will map an enum of type T from an IList<T> property to a comma delimited string in the database and back again. There's no easy way to restrict T to an enum but the code will only work with enums.
Mapping a property using the custom type is simple with Fluent NHibernate:
public class Person
{
public Person()
{
IncomeTypes = new List<IncomeType>();
}
public virtual int PersonId { get; protected set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual IList<IncomeType> IncomeTypes { get; protected set; }
}
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Person");
Id(x => x.PersonId).GeneratedBy.Identity();
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.IncomeTypes).CustomType<EnumAsDelimitedStringType<IncomeType>>();
}
}
And here's the code for the user type:
public class EnumAsDelimitedStringType<T> : IUserType
{
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, y))
{
return true;
}
var xList = x as IList<T>;
var yList = y as IList<T>;
if (xList == null || yList == null)
{
return false;
}
// compare set contents
return xList.OrderBy(xValue => xValue).SequenceEqual(yList.OrderBy(yValue => yValue));
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var outValue = NHibernateUtil.AnsiString.NullSafeGet(rs, names[0]) as string;
if (string.IsNullOrEmpty(outValue))
{
return new List<T>();
}
var getValueArray = outValue.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
return Array.ConvertAll(getValueArray, s => (T)Enum.Parse(typeof(T), s)).ToList();
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var inValue = value as IList<T>;
// set to string.Empty if you prefer to store that instead of null when the collection is null or empty
object setValue = null;
if (inValue != null && inValue.Any())
{
var setValueArray = Array.ConvertAll(inValue.ToArray(), v => Enum.GetName(typeof(T), v));
setValue = string.Join(",", setValueArray);
}
NHibernateUtil.AnsiString.NullSafeSet(cmd, setValue, index);
}
public object DeepCopy(object value)
{
return value;
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return cached;
}
public object Disassemble(object value)
{
return value;
}
public SqlType[] SqlTypes
{
get { return new[] {new SqlType(DbType.AnsiString)}; }
}
public Type ReturnedType
{
get { return typeof(IList<T>); }
}
public bool IsMutable
{
get { return false; }
}
}
I think you're on the right track pursuing a [Flags] enum. You may have done this, but just in case -- making an enum flags-worthy is more than adding the attribute. You also have to specify the value for the items in a binary-friendly manner. I've found the easiest way to do this is as follows:
[Flags]
public enum IncomeType : long // you'll need the room with several options
{
FullTime = 1,
PartTime = 1 << 1,
SelfEmployed = 1 << 2
// And so on
}
If you don't do this, then you'll get sequential integer values, which breaks the bitwise comparison that allows you to do multiple values in a single integer.
Your code to create the SelectList looks fine. Your options should construct form values that get posted back with the same name. If you want to use the default modelbinder, that means the associated property on your view model would need to be List<int>. If you're not using a view model (you probably should) you can pull it out of the forms collection.
Once you have this set up, then translating from your view model to your NHibernate entity is simple if a little annoying. You basically have to cycle through the values in the list and |= them onto your NHibernate entity's single enum property.
So let's assume you have a view model like this:
public class MyEditViewModel
{
public string Name { get; set; }
public List<int> IncomeSelections { get; set; }
// You'll probably have this to populate the initial view rendering
public SelectList AllIncomeOptions { get; set; }
}
You'll build your view using your helpers and all that, then build the checkboxes using the SelectList but making sure the input name is IncomeSelections, then when it's posted back you will push the view model data into your NHibernate entity something like this:
var myNHEntity = new NHEntity();
// If you're editing an existing entity, then be sure to reset the enum
// value to 0 before going into the following foreach loop...
foreach (var incomeSelection in viewModel.IncomeSelections)
{
myNHEntity.IncomeSelection |= incomeSelection;
}
There's probably a more clever way to do this, and you might have to cast the int to your enum type, but you'll figure that out (I'd do it for you, but it is Friday and I already have a beer open).
NHibernate should persist it without you having to do anything funky on the NH side.
In summary...
It seems like this is more a problem of how you handle the posted data than the NHibernate side. If you implement something like this, then be sure to use Fiddler or FireBug to inspect the posted values to make sure 1) they're integers and 2) the names are the same so they'll be added to the list.
Good luck!
The problem is simply that it won't be able to map a List without building a full relationship with an intermediate association table. It is way simpler to have the record store the values as a comma-separated string (so your record property is a string, not a list of string) and your part can map back and forth between string and List.
You can find an example of something very close here:
https://bitbucket.org/bleroy/nwazet.commerce/src/d722cbebea525203b22c445905c9f28d2af7db46/Models/ProductAttributesPartRecord.cs?at=default
https://bitbucket.org/bleroy/nwazet.commerce/src/d722cbebea525203b22c445905c9f28d2af7db46/Models/ProductAttributesPart.cs?at=default
It's not using enum values, instead it's a list of ids, but that should give you a good idea about how to make this work fairly simply: parsing enums you already know how to do.
Let me know if you need more details, but I think that's what you needed to get unblocked.

MVC 3 Reusable Remote Validation with DB Lookup

I have an MVC 3 appliation which I have many integer fields on a form. They all require range validation but the ranges exists in a table in my database. So I would like to create a reusable remote validation tool which will look up the min and max value and return the validation to the view.
I am updating this with some example code of what I would like to do to see my request might clarify what I am looking for:
In my validation class:
[Remote("CheckIntegerRange", "Validation", ErrorMessage = "Value outside of range")]
public object UW1_Web_Tension_SP { get; set; }
[Remote("CheckIntegerRange", "Validation", ErrorMessage = "Value outside of range")]
public object UW2_Web_Tension_SP { get; set; }
[Remote("CheckIntegerRange", "Validation", ErrorMessage = "Value outside of range")]
public object UW3_Web_Tension_SP { get; set; }
In my ValidationController I tried to create a function with multiple parameters but I dont think I can - however I think it shows what I am trying to do more clearly:
public class ValidationController : Controller
{
public JsonResult CheckIntegerRange(int integer, string EntityName, string AttributeName)
{
var result = false;
int MinInteger = 0;
int MaxInteger = 100;
//declare recipe entities
var context = new MadicoRecipeEntities();
//set sql statements and get description, etc from attributes view
var esqlIntegerAttributeDetails = "SELECT VALUE c FROM MadicoRecipeEntities.v_AttributeIntegerRangeDetails AS c " +
"WHERE c.EntityName = '" + EntityName + "' AND c.Attribute = '" + AttributeName + "'";
var queryAttributeDetails = context.CreateQuery<v_AttributeIntegerRangeDetails>(esqlIntegerAttributeDetails);
var RecipeAttributes = queryAttributeDetails.ToList();
foreach (var AttributeDetails in RecipeAttributes)
{
MinInteger = AttributeDetails.Min;
MaxInteger = AttributeDetails.Max;
}
return Json(result, JsonRequestBehavior.AllowGet);
}
}
I have found the following post on the asp.net forums which handled my first obstacle - passing different named attributes to the same validator. However, in this example the name is passed in generically - I need to have the name in order to query the table to get the applicable min and max ranges.
http://forums.asp.net/t/1625928.aspx/3/10
Can anyone point me in the right direction?
Thanks!
It is called remote validation! Here is an example:
remote validation

Resources