I have a ViewModel with a number of different properties (ie string, int, etc) that I need to iterate through in the controller. What is the best way to do this? Here is the ViewModel's defintion:
public class BankListViewModel
{
public int ID { get; set; }
public string BankName { get; set; }
public string EPURL { get; set; }
public string AssociatedTPMBD { get; set; }
public string Tier { get; set; }
public List<BankListAgentId> BankListAgentId { get; set; }
public List<BankListStateCode> BankListStateCode { get; set; }
}
I need to omit the two lists, however. Any ideas?
EDIT
The purpose of this process is to pass specific items of the view model into three separate objects. The view model was created to combine properties of three separate SQL tables/Models. I am now trying to divide them up appropriately and add the information to the relevant tables. Right now I'm simply going one by one like so:
BankListMaster banklistmaster = new BankListMaster();
banklistmaster.AssociatedTPMBD = viewmodel.AssociatedTPMBD;
banklistmaster.BankName = viewmodel.BankName;
Although it's not clear why you would need to iterate over the properties instead of just reading their vaules, you could accomplish this using reflection
var model = new BankListViewModel();
PropertyInfo[] properties = model.GetType().GetProperties();
foreach (var property in properties)
{
if (property.GetType() != typeof(List<BankListAgentId>))
{
//do your thing here
}
}
Related
Useing Entity framework I want to include an only the first level of children objects and not the children of child
I have these two classes:
public class BusinessesTBL
{
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
public ICollection<OffersTBL> OffersTBLs { get; set; }
}
public class OffersTBL
{
public int ID { get; set; }
public string Name { get; set; }
public int CatId { get; set; }
public string BusinessesTBLID { get; set; }
public virtual BusinessesTBL BusinessesTBLs { get; set; }
}
when I try to bring all offers according to CatId field, I need to return the BusinessesTBLs also, but the method also return offers again per each BusinessesTBL obj , My code is :
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Include(s => s.BusinessesTBLs);
}
You can see the wrong result on :
http://mycustom.azurewebsites.net/api/OffersApi/GetOffersTBLsCat/4
As you can see it return all offers under each Business object while business object under each offer, And I want only to return offers with its Business object without offer under Business obj.
Could anyone help please?
I now see that a big part of the original answer is nonsense.
Sure enough, the reason for the endless loop is relationship fixup. But you can't stop EF from doing that. Even when using AsNoTracking, EF performs relationship fixup in the objects that are materialized in one query. Thus, your query with Include will result in fully populated navigation properties OffersTBLs and BusinessesTBLs.
The message is simple: if you don't want these reference loops in your results, you have to project to a view model or DTO class, as in one of the other answers. An alternative, less attractive in my opinion, when serialization is in play, is to configure the serializer to ignore reference loops. Yet another less attractive alternative is to get the objects separately with AsNoTracking and selectively populate navigation properties yourself.
Original answer:
This happens because Entity Framework performs relationship fixup, which is the process that auto-populates navigation properties when the objects that belong there are present in the context. So with a circular references you could drill down navigation properties endlessly even when lazy loading is disabled. The Json serializer does exactly that (but apparently it's instructed to deal with circular references, so it isn't trapped in an endless loop).
The trick is to prevent relationship fixup from ever happing. Relationship fixup relies on the context's ChangeTracker, which caches objects to track their changes and associations. But if there's nothing to be tracked, there's nothing to fixup. You can stop tracking by calling AsNoTracking():
db.OffersTBLs.Include(s => s.BusinessesTBLs)
.AsNoTracking()
If besides that you also disable lazy loading on the context (by setting contextConfiguration.LazyLoadingEnabled = false) you will see that only OffersTBL.BusinessesTBLs are populated in the Json string and that BusinessesTBL.OffersTBLs are empty arrays.
A bonus is that AsNoTracking() increases performance, because the change tracker isn't busy tracking all objects EF materializes. In fact, you should always use it in a disconnected setting.
You have deactivated lazy loading on OffersTBLs making it non-virtual. What if you activate lazy loading? like this:
public class BusinessesTBL
{
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
//put a virtual here
public virtual ICollection<OffersTBL> OffersTBLs { get; set; }
}
Then, be sure to not call/include OffersTBLs when serializing. If the OffersTBLs are still returning, it is because you are fetching them somewhere in your code. If this is happening, edit your question and paste all the code, including the serializing logic.
Since OffersTBL has an association to BusinessesTBL and BusinessesTBL to OffersTBL you can loop infinitly throw the Entities like OffersTBL.BusinessesTBL.OffersTBL.BusinessesTBL and so on.
To control the nested depth of the Entities i'm usually using helperclasses with the needed properties in them.
For BusinessesTBL
public class BusinessesTBLHelper
{
private BusinessesTBLHelper(BusinessesTBL o){
ID = o.ID;
FirstName = o.FirstName;
lastName = o.LastName;
OffersTBLids = new List<int>();
foreach(OffersTBL offersTbl in o.OffersTBLs){
OffersTBLids.Add(offersTbl.ID);
}
}
public string ID { get; set; }
public string FirstName { get; set; }
public string lastName { get; set; }
public IEnumerable<int> OffersTBLids { get; set; } //no references anymore
}
And same for your OffersTBL Entity.
public class OffersTBLHelper
{
private OffersTBLHelper(OffersTBL o){
ID = o.ID;
Name = o.Name;
CatId = o.CatId;
BusinessesTBLID = o.BusinessesTBLID;
BusinessesTBLs = new BusinessesTBLHelper(o.BusinessesTBLs);
}
public string ID { get; set; }
public string Name{ get; set; }
public intCatId{ get; set; }
public string BusinessesTBLID { get; set; }
public BusinessesTBLHelper BusinessesTBLs { get; set; }
}
On quering database you can directly create the new helperobjects from queryresult:
public IEnumerable<OffersTBLHelper> GetOffersTBLsCat(int id)
{
return db.OffersTBLs.where(s => s.CatId == id).Select(x=> new OffersTBLHelper(x)).ToList();
}
Now you have all the OffersTBL with BusinessesTBLs under. The loop stops here because the BusinessesTBLs have no OffersTBL under it. However, it only has them Ids in a List for further referencing and identifying.
Assuming that the object isnt null and just empty:
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Include(s => s.BusinessesTBLs).Where(x => !x.BusinessesTBLs.OffersTBLs.Any());
}
Edit: Filter before the include:
public IQueryable<OffersTBL> GetOffersTBLsCat(int id)
{
db.OffersTBLs.Where(x => !x.BusinessesTBLs.OffersTBLs.Any())
.Include(s => s.BusinessesTBLs);
}
I have three objects:
public class Part
{
[Key]
public int ID { get; set; }
public string Number { get; set; }
public string Name { get; set; }
public int descID { get; set; }
}
public class Description
{
[Key]
public int descID { get; set; }
public string descName { get; set; }
public string description { get; set; }
}
public class GridPart
{
public string name{ get; set; }
public string number { get; set; }
public string description { get; set; }
}
I'm using LINQ to join Part and Description on the descID column:
public ActionResult Index()
{
var myParts = from p in db.Parts
join d in db.Description on p.descID equals d.DescriptorID
select new { Description = d.description, Name = p.name};
List<GridPart> partsList = new List<GridPart>();
foreach (var m in myParts)
{
GridPart gp = new GridPart();
gp.Name = m.name;
gp.description = m.description;
partsList.Add(gp);
}
return View(partsList);
}
If I was just using the Parts table, in the view I would do:
#model IEnumerable<MyApp.Models.Part>
What do I do if I'm using the joined table? This uses both Parts and Description, not to mention my List of GridParts, how do I pass this through to display all of the data I need?
Any advice is greatly appreciated.
If you pass an anonymous type to your view it won't be strongly typed. You can refer to your model like this instead
#Html.TextBox("Name")
or
#Html.Display("Name")
Although this will work I would advice against it - The better solution would be to go with a Viewmodel instead. This will make your view strongly typed.
Edit: Looking on this again I see that your actually not parsing the anonymous type to your view. Your parsing a list of GridParts.
You should be able to strongly type your view like your tried - just refer to GridParts instead of parts.
#model IEnumerable<MyApp.Models.GridPart>
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
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.
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