I am developing a blog application in ASP.NET MVC and I have doubt whether my solution of updating some related classes is fully correct.
I have a class representing texts:
public partial class Text
{
public long ID { get; set; }
public string Title { get; set; }
...
public virtual ICollection<Tag> Tags { get; set; }
}
and tags:
public partial class Tag
{
public long ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Text> Texts { get; set; }
}
When I'm editing a texts, the TextEditorViewModel is being passed:
public class TextEditorViewModel
{
public long ID { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public IEnumerable<TagEditorViewModel> Tags { get; set; }
}
TagEditorViewModel is:
public class TagEditorViewModel
{
public long ID { get; set; }
public string Name { get; set; }
public bool IsInText { get; set; }
}
In the view I'm checking appropiate tags (this is reflected by IsInText property) and this works fine and correct data is being passed back to the controller.
Here is my code for the repository on which controller is working:
Text OriginalText = Mapper.Map<Text>(Text);
IEnumerable<long> tags = OriginalText.Tags.Select(tag => tag.ID);
ICollection<Tag> tagobj = context.Tags.Where(tag => tags.Contains(tag.ID)).ToList();
OriginalText.Tags = tagobj;
Text is object of TextEditorViewModel which is passed back on post. I'm using mapper to bind only primitive types and I'm rewriting all collection and reference types (as seen above), only tags marked as not in the text are filtered out. Is there any simpler way of managing the tags instead of quering the database and rewriting them?
You can attach the tags manually to the context instead of reloading them from the database because you have the ID of the tags which is sufficient to create/update a relationship:
Text OriginalText = Mapper.Map<Text>(Text);
foreach (var tag in OriginalText.Tags)
context.Tags.Attach(tag);
context.Texts.Add(OriginalText);
context.SaveChanges();
(I've taken the last two lines from your comment.)
Even though the properties in the different tags don't have the values from the database (except the ID) it will work because EF only needs the correct tag ID to create the relationship.
Related
I'm having trouble understanding how to implement a ViewModel in Asp.net MVC, I have the following tables:
Form
ID, Data
Report
ID, FormID, Owner, Category, Status, SubmissionDate
ReportValues
ID, ReportID, Title, Value
I'm looking for a way to display and edit Report and ReportValues in the one ViewModel where ReportValues.ReportID = Report.ID
ReportValues will have multiple entries that relate to a Report.
I have had a look at similiar questions on here and tried following a tutorial ( http://techfunda.com/howto/262/list-data-using-viewmodel ) and coming up empty handed.
If you need any more information let me know and thanks in advance for any replies!
Your View Model is nothing more than a class. You can solve this many ways, but here's an example.
Create your 3 classes like you normally would.
public class Form
{
public int Id { get; set; }
public string Data { get; set; }
}
public class ReportValues
{
public int Id { get; set; }
public int ReportId { get; set; }
public string Title { get; set; }
public string Value { get; set; }
}
public class Report
{
public int Id { get; set; }
public int FormId { get; set; }
public string Owner { get; set; }
public string Category { get; set; }
public string Status { get; set; }
public DateTime SubmissionDate { get; set; }
}
Then, create your ViewModel class to include the three above classes like this.
public class ReportViewModel
{
public Form Form { get; set; }
public ReportValues ReportValues { get; set; }
public Report Report { get; set; }
}
In your view you can access your three classes and their properties as you would in your controller. Model.Form.Id
Depending on your data types, ReportValues will likely be a property of Report, but that's entirely up to your data structure. You will need to populate the classes using whatever method you want (Entity Framework, ADO, etc.) before you can pass them to your view and use them.
I am new to MVC and was trying to get the Scaffolding to automagically create the drop down list in the PersonTitle for Person and Title. I got the drop down list to work however, the scaffolding function takes the first instance of a string as the value of the drop down. As in the case of the Person, the first name is used. However, that is not very informative.
I want to be able to modified it to contain the First and Last name. What is the best solution for this? I tried adding a FullName field but that has its own set of problems. Once of them being, I don't want the program/user to handle extra data entries.
Is there a better method of creating the drop down list for Person and Title after the scaffolding completes? What is the best way to add foreign key drop down list from other tables?
It would help if you provide code samples as I am new to MVC and the best practices solution as I would have to do this to several other tables.
Thanks much.
A person can have many titles
// Title -->>PersonTitle<<<----Person
public class PersonTitle
{
//person can have many titles
public int PersonTitleID { get; set; }
public int PersonID { get; set; }
public int TitleID { get; set; }
public virtual Person Person { get; set; }
public virtual Title Title { get; set; }
}
List of Titles
public class Title
{
public int TitleID { get; set; }
[Display(Name = "Title Name")]
public string TitleName { get; set; } //drop down text
public virtual ICollection<PersonTitle> PersonTitles { get; set; }
}
List of People
public class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; } //drop down text is used
public string LastName { get; set; }
public virtual ICollection<PersonTitle> PersonTitles { get; set; }
}
I'm making a simple website where I'm storing some information that I get from an excel file into my models class and retrieving them from the html page. The following class is a class in my models:
public class ToxinInformation
{
public string cas_rn { get; set; }
public string critical_effect { get; set; }
public string point_of_departure { get; set; }
public string adi_tdi { get; set; }
public string intake { get; set; }
public string hazard_quotient { get; set; }
public string comment { get; set; }
public string tox_link { get; set; }
public string tox_link_decription { get; set; }
public string intake_link { get; set; }
public string intake_link_description { get; set; }
public IList<string> Links { get; set; }
}
And I use this code to set the information in my controller class and return the view:
(of course I would set information all the variables, not only the first one)
var model = new ToxinInformation
{
cas_rn = "lol"
};
return View(model);
So far I can easily set all my strings and my list and retrieve them on my html page, but what do I do if in some cases I need several instances of the class "ToxinInformation"? In some cases I have 2 or more set of data I'd like to save and show in HTML except for just one.
Any suggestions would be very helpful.
You should make a list and add instances of model to the list. Then you can use a DisplayFor or EditorFor template to show them all.
var models = new List<ToxinInformation>();
foreach(var dataBlob in YourDataStore)
{
var model = new ToxinInformation()
{
cas_rn = dataBlob.cas_rn // Not sure where your raw data is coming from.
}
models.add(model)
}
return View(models);
I have a DbDataController which delivers a List of Equipment.
public IQueryable<BettrFit.Models.Equipment> GetEquipment() {
var q= DbContext.EquipmentSet.OrderBy(e => e.Name);
return q;
}
In my scaffolded view everything looks ok.
But the Equipment contains a HashSet member of EquipmentType. I want to show this type in my view and also be able to add data to the EquipmentType collection of Equipment (via a multiselect list).
But if I try to include the "EquipmentType" in my linq query it fails during serialisation.
public IQueryable<BettrFit.Models.Equipment> GetEquipment() {
var q= DbContext.EquipmentSet.Include("EquipmentType").OrderBy(e => e.Name);
return q;
}
"Object Graph for Type EquipmentType Contains Cycles and Cannot be Serialized if Reference Tracking is Disabled"
How can I switch on the "backtracking of references"?
Maybe the problem is that the EquipmentType is back-linking through a HashSet? But I do not .include("EquipmentType.Equipment") in my query. So that should be ok.
How is Upshot generating the model? I only find the EquipmentViewModel.js file but this does not contain any model members.
Here are my model classes:
public class Equipment
{
public Equipment()
{
this.Exercise = new HashSet<Exercise>();
this.EquipmentType = new HashSet<EquipmentType>();
this.UserDetails = new HashSet<UserDetails>();
}
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Picture { get; set; }
public string Link { get; set; }
public string Producer { get; set; }
public string Video { get; set; }
public virtual ICollection<EquipmentType> EquipmentType { get; set; }
public virtual ICollection<UserDetails> UserDetails { get; set; }
}
public class EquipmentType
{
public EquipmentType()
{
this.Equipment = new HashSet<Equipment>();
this.UserDetails = new HashSet<UserDetails>();
}
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Equipment> Equipment { get; set; }
public virtual ICollection<UserDetails> UserDetails { get; set; }
}
try decorating one of the navigation properties with [IgnoreDataMember]
[IgnoreDataMember]
public virtual ICollection<Equipment> Equipment { get; set; }
The model generated by upshot can be found on the page itself. In your Index view you will see the UpshotContext HTML helper being used (given that you are using the latest SPA version), in which the dataSource and model type are specified.
When the page is then rendered in the browser, this helper code is replaced with the actual model definition. To see that, view the source code of your page in the browser and search for a <script> tag that starts with upshot.dataSources = upshot.dataSources || {};
Check here for more info about how upshot generates the client side model.
As for the "backtracking of references", I don't know :)
I figured out - partially how to solve the circular reference problem.
I just iterated over my queried collection (with Include() ) and set the backreferences to the parent to NULL. That worked for the serialisation issue which otherwise already breaks on the server.
The only problem now is the update of a data entity - its failing because the arrays of the referenced entitycollection are static...
To solve the cyclic backreference, you can use the IgnoreDataMember attribute. Or you can set the back reference to NULL before returning the data from the DbDataController
I posted a working solution to your problem in a different question, but using Entity Framework Code First.
https://stackoverflow.com/a/10010695/1226140
Here I show how to generate your client-side model manually, allowing to you to map the data however you please
I am developing a application for Sales Order Management using ASP.NET MVC 3.0. I need to develop a page where Customer Details can be added.
Customer Details Include
public class Customer
{
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Alias { get; set; }
public int DefaultCreditPeriod { get; set; }
public Accounts Accounts { get; set; }
public IList<Address> Addresses { get; set; }
public IList<Contact> Contacts { get; set; }
}
public class Accounts
{
public int ID { get; set; }
public string VATNo { get; set; }
public string CSTNo { get; set; }
public string PANNo { get; set; }
public string TANNo { get; set; }
public string ECCNo { get; set; }
public string ExciseNo { get; set; }
public string ServiceTaxNo { get; set; }
public bool IsServiceTaxApplicable { get; set; }
public bool IsTDSDeductable { get; set; }
public bool IsTCSApplicable { get; set; }
}
public class Address
{
public int ID { get; set; }
public AddressType Type { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Line4 { get; set; }
public string Country { get; set; }
public string PostCode { get; set; }
}
public class Contact
{
public int ID { get; set; }
public ContactType Type { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string PhoneNumber { get; set; }
public string Extension { get; set; }
public string MobileNumber { get; set; }
public string EmailId { get; set; }
public string FaxNumber { get; set; }
public string Website { get; set; }
}
Customer Requires a single page to fill all the customer details(General info, Account Info,Address Info and Contact Info). There will be multiple Addresses(Billing, Shipping, etc) and multiple Contacts (Sales, Purchase). I am new to MVC. How to Create the View for the above and Add multiple Address dynamically?
I often create wrapper models to handle this kind of situation e.g.
public class CustomerWrapperModel
{
public Customer Customer { get; set;}
public Accounts Accounts { get; set;}
public List<Address> AddressList { get; set}
//Add
public CustomerWrapperModel()
{
}
//Add/Edit
public CustomerWrapperModel(Customer customer, Accounts accounts, List<Address> addressList)
{
this.Customer = customer;
this.Accounts = accounts;
this.AddressList = addressList;
}
}
then declare the View to be of type CustomerWrapperModel and use editors like so:
#model MyNamespace.CustomerWrapperModel
#Html.EditorFor(model => model.Customer)
#Html.EditorFor(model => model.Accounts)
#Html.EditorFor(model => model.AddressList)
and have a controller to receive the post that looks like this:
[HttpPost]
public ActionResult(Customer customer, Accounts accounts, List<Address> addressList)
{
//Handle db stuff here
}
As far as adding addresses dynamically I found the best way to do this if you're using MVC validation and want to keep the list structured correctly with the right list indexes so that you can have the List parameter in your controller is to post the current Addresses to a helper controller like this:
[HttpPost]
public PartialResult AddAddress(List<Address> addressList)
{
addressList.Add(new Address);
return PartialView(addressList);
}
then have a partial view that just renders out the address fields again:
#model List<MyNamespace.Address>
#{
//Hack to get validation on form fields
ViewContext.FormContext = new FormContext();
}
#Html.EditorForModel()
make sure you address fields are all in one container and then you can just overwrite the existing ones with the returned data and your new address fields will be appended at the bottom. Once you have updated your container you can do something like this to rewire the validation:
var data = $("form").serialize();
$.post("/Customer/AddAddress", data, function (data) {
$("#address-container").html(data);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
});
NB. I know some people with have an issue with doing it this way as it requires a server side hit to add fields to a page that could easily just be added client side (I always used to do it all client side but tried it once with this method and have never gone back). The reason I do it this way is because it's the easiest way to keep the indexes on the list items correct especially if you have inserts as well as add and your objects have a lot of properties. Also, by using the partial view to render the data you can ensure that the validation is generated on the new fields for you out of the box instead of having to hand carve the validation for the newly added client side fields. The trade off is in most cases a minor amount of data being transferred during the ajax request.
You may also choose to be more refined with the fields you send to the AddAddress controller, as you can see I just post the entire form to the controller and ignore everything but the Address fields, I am using fast servers and the additional (minor) overhead of the unwanted form fields is negligible compared to the time I could waste coding this type of functionality in a more bandwidth efficient manner.
You pass your root model object to the View call in your controller like this:
public ActionResult Index() {
var customer = GetCustomer(); // returns a Customer
return View(customer);
}
And then your view looks something like this:
#model Customer
<!DOCTYPE html>
<!-- etc., etc. -->
<h1>Customer #Model.Name</h1>
<ul>
#foreach (var address in Model.Addresses) {
<li>#address.Line1</li>
}
</ul>
One gets the picture.
The code above depends on the #model directive, which is new in ASP.NET MVC 3 (see this blog post).
Is a good question :D for normal navigation properties such as Accounts doing this is not to hard:
#Html.EditorFor(model => model.Accounts.ID)
#Html.EditorFor(model => model.Accounts.VATNo)
will do something you want. But for collection navigation properties (Addresses and Contacts) you can't do this in one place by default. I suggest you use a different page for Addresses (and one for Contacts). Because it is the easiest way. But if you want to do this in one place (and also with out AJAX requests), you can create view by Customer, use scaffolding for model and it's simple navigation properties, and for lists (Addresses, Contacts) you must add them with JavaScript to the input fields (for example for each Address added, put it in an Array) and post fields to server. At server you can get main model and simple properties by default model-binder and for lists, you can 1) create your own model binder 2) parse them from inputted strings by yourself. Good lock