Binding IList<IMyInterfaceType> doesn't display members of Interfaces that IMyInterface inherits - data-binding

I'm binding IList to a GridView. IMyInterface looks like
public interface IMyInterface: IHasTotalHours, IHasLines
{
DateTime GoalStartDate { get; set; }
DateTime GoalEndDate { get; set; }
}
I bind an instance to a Grid like this:
IList<IMyInterface> instance= GetMyData();
myGrid.DataSource = instance;
myGrid.DataBind();
When bind this to the grid, the only members that show up in the grid are the direct members of IMyInterface: GoalStartDate and GoalEndDate.
Why is that? How do I get the grid to display the members of the other interfaces it inherits?
Update
The inherited interfaces define simple data properties like
public interface IHasTotalHours
{
string Description { get; set; }
int Hours{ get; set; }
}
public interface IHasLines
{
double TotalLines { get; set; }
double LinesPerHour { get; set; }
}
There is a class that implements IMyInterface:
public class MyClass : IMyInterface
{
public string Description { get; set; }
public int Hours { get; set; }
public double TotalLines { get; set; }
public double LinesPerHour { get; set; }
public DateTime GoalStartDate { get; set; }
public DateTime GoalEndDate { get; set; }
}
These are cast as IMyInterface, and returned in the list that I'm binding to the GridView.

Data bound controls do not use reflection but a TypeDescriptor to get the properties from a data source. In the TypeDescriptor.GetProperties method, you can read the following:
The properties for a component can
differ from the properties of a class,
because the site can add or remove
properties if the component is sited.
Apparently the default implementation will only return direct properties from an Interface and not the inherited ones.
Luckily this mechanism is extensible, and you can write a TypeConverter class with custom property information implementation. Please refer to the remarks in the TypeConverter documentation for implementing property logic.
The GetProperties implementation of your custom TypeConverter class can call TypeDescriptor.GetProperties(Type) on your interface and all it's inherited interfaces. But maybe you could even write a generic TypeConverter that would find all inherited properties by using reflection.
Then you attach this custom TypeConverter to your interface with the TypeConverterAttribute attribute.
And then, like magic, the data source will find all properties. ;-)

It's because an interface is a contract, and that's the only way to interact with an object is through that specific contract. The other interfaces cannot be assumed and can't be utilized until a cast is made.
So when you bind a List of T to something, the datagrid doesn't know about those other interfaces. And the datagrid isn't going to use reflection to figure out what other classes or interfaces might be inherited. The only object properties that are going to be available to the datagrid are the properties exposed by the T interface.
You need to bind List if you want the datagrid to have access to all the properties.

Related

How to specify default property values for owned entity types in Entity Framework Core 2.0?

I have a simple POCO type, say something like
public class OwnedEntity {
public string stringProperty { get; set; }
public decimal decimalProperty { get; set; }
public bool boolProperty { get; set; }
public int intProperty { get; set; }
}
and an actual entity with an OwnedEntity reference
public class SomeEntity {
public string Id { get; set; }
public OwnedEntity OwnedEntity { get; set; }
}
I set up the relationship like described in the documentation using EF Core's Fluent API:
protected override void OnModelCreating (ModelBuilder builder) {
base.OnModelCreating (builder);
builder.Entity<SomeEntity> ().OwnsOne (e => e.OwnedEntity);
}
I can't find anything on how to define default-values for all the properties of OwnedEntity. I tried to initialize the properties like this:
public class OwnedEntity {
public string stringProperty { get; set; } = "initial"
public decimal decimalProperty { get; set; } = -1M;
public bool boolProperty { get; set; } = false;
public int intProperty { get; set; } = -1;
}
but with no effect. Same goes with the [DefaultValueAttribute] (but that was to expect since it's explicitly mentioned).
There's a bit of information on how to handle initial values for regular entities:
modelBuilder.Entity<SomeOtherEntity>()
.Property(e => e.SomeIntProperty)
.HasDefaultValue(3);
But since I'm facing an Owned Entity Type, I can't access the type via Entity<T>.
Is there a way of doing what I'm looking for?
Some things worth mentioning:
I have a solid amount of specific entities where most of them are using the OwnsOne relation
Declaring all OwnedEntity-properties in a base class is not an option since not all the entities have those properties
I`m using EF Core 2.0.3 and ASP.NET Core MVC 2.0.4
Edit:
Originally, I wanted to have newly created SomeEntity instances to come with preset properties for all of the 'embedded' SomeEntity.OwnedEntity properties.
But looking at how my associated controller works, it all makes sense... I have the following methods for the 'Create' operation:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create (SomeEntity model) {
context.Add (model);
await context.SaveChangesAsync ();
// redirect etc.
}
Which means that no object is created for the [HttGet] overload of Create and all the HTML inputs linked to properties (via asp-for) are initially empty. Okay. So I guess the proper way of doing this is to manually create a new instance of SomeEntity and pass it to the Create view like this:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create), new SomeEntity());
}
Is this the right approach then or are there some more things to keep in mind?
Assuming you understand what EF Core Default Values are for, and just looking for equivalent of Entity<T>().Property(...) equivalent.
The owned entities are always configured for each owner type by using the ReferenceOwnershipBuilder<TEntity,TRelatedEntity> class methods. To access this class you either use the result of OwnsOne method, or use the OwnsOne overload taking second argument of type Action<ReferenceOwnershipBuilder<TEntity,TRelatedEntity>>.
For instance, using the second approach:
builder.Entity<SomeEntity>().OwnsOne(e => e.OwnedEntity, ob =>
{
ob.Property(e => e.stringProperty)
.HasDefaultValue("initial");
ob.Property(e => e.decimalProperty)
.HasDefaultValue(-1M);
// etc.
});

Using (showing) data from another model from my current view

No code to show. I just want to understand something. I already do some MVC code (I have a model, I ask Visual Studio to create Controller and View).
Each view has only "ONE MODEL" associated. So, with Razor, I can show data from this model. I play with my code and I understand it up to now.
BUT ...
On the same view, HOW we can work with another MODEL ?
For me, a model is simply a class with properties, etc. My database has an equivalent "data table" for each model. I can manipulate it with Entity Framework ... no problem. But, I need to use DATA from different model (different table) in the SAME VIEW and Visual Studio does not give me permission to use another MODEL in the view.
What is the strategy ? (or maybe I don't understand something ...)
Thank you.
The strategy is to build a view model, a model built to be displayed, and represents the data that you need to use.
Example :
You have these classes, classes who are a representation of your database :
public class FootballTeam{
public string Name{get;set;}
public string Logo{get;set;}
}
public class FootballGame{
public Datetime Date {get;set;}
public string Competition {get;set;}
}
public class Referee{
public string Name{get;set;}
public int Experience {get;set;}
}
To display information about a match game, you can create a view model for this, class who can references some classes of your business model if necessary :
public class GameViewModel{
[DisplayName("Home team")]
public FootballTeam HomeTeam{get;set;}
[DisplayName("Referee")]
public Referee Referee{get;set;}
[DisplayName("Visitor team")]
public FootballTeam VisitorTeam {get;set;}
[DisplayName("Comments")]
public List<string> RedactionComments{get;set;}
}
And create a view who will consume this GameViewModel.
In general, when you create a new MVC project, your have a folder named "ViewModels" in your presentation layer who contains some classes like this one.
This method allows to separate your business model to your presentation model, which are 2 completely different things.
There are very good answers here : What is ViewModel in MVC?
You can update your model type of your razor view to any type you want. It will work as long as you are passing that type from your action method.
Simply open up the razor view and change the line where it says what type the model is.
#model Customer
Now you need to make sure that you are passing a Customer object from your action
public ActionResult Create()
{
return View( new Customer());
}
Also when you create a view, You do not need to necessarily select the Model type in the Dialog box. You can keep that empty and add it to the razor view as needed ( as shown above)
If you want to bring data from 2 different table, Create a new view model which has properties needed for the view and use that as your view's model type.
You should use ViewModal to Create a ViewModal that will be combination of two modals properties as per our need
ViewModel contains the fields which are represented in the strongly-typed view. It is used to pass data from controller to strongly-typed view with Own Defined Modals
Understand how to use View Modal in MVC From Link Below -
Understand View Modal In MVC
CODE THAT DEMONSTRATE HOW TO USE VIEWMODALS IN MVC
Product.cs
public class Product
{
public Product() { Id = Guid.NewGuid(); }
public Guid Id { get; set; }
public string ProductName { get; set; }
public virtual ProductCategory ProductCategory { get; set; }
}
ProductCategory.cs
public class ProductCategory
{
public int Id { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
ProductViewModel.cs
public class ProductViewModel
{
public Guid Id { get; set; }
[Required(ErrorMessage = "required")]
public string ProductName { get; set; }
public int SelectedValue { get; set; }
public virtual ProductCategory ProductCategory { get; set; }
[DisplayName("Product Category")]
public virtual ICollection<ProductCategory> ProductCategories { get; set; }
}

Is there a way to specify .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None) as a decorator to my entity?

I have the following class. I was using a mapping file but I would not like to decorate the class with the different options. I already have in my mapping file:
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
How can I set this or set another option of DatabaseGeneratedOption by decorating the class? I looked at the Intellisense options but can't find one for this all I can find is [DatabaseGenerated()] and I am not sure if that's correct or how to set that option:
[DatabaseGenerated()]
public class ContentType : Entity
{
public ContentType()
{
this.Contents = new List<Content>();
}
[Key]
public int ContentTypeId { get; set; }
public string Name { get; set; }
public virtual ICollection<Content> Contents { get; set; }
}
DatabaseGenerated is Property and Field specific attribute. You can't add it to a class, only specific properties or fields.

Map all properties of a class using reflection

I have two domain classes
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string HouseName { get; set; }
public string StreetName { get; set; }
public string PinCode { get; set; }
}
I want to map object of Employee class to another class.
I am using reflection to map empData object to another object. The code i used is
private void GetValues(object empData)
{
System.Type type = empData.GetType();
foreach (PropertyInfo pInfo in type.GetProperties())
{
//do some stuff using this pInfo.
}
}
I could easily map all the properties except the Address property in the emp object which is an object of another class.
So how can i map all the properties irrespective of its type ? i.e, if address contains object of another class it should also get mapped.
Can't you use AutoMapper for mapping classes?
You can know the type of property you are mapping by
if (propertyInfo.PropertyType == typeof(Address))
{ // do now get all properties of this object and map them}
Assuming that you want to be able to do this on any type of object and not just this specific one, you should use some sort of recursive solution. However if it's just for this object - why are you even using reflection? To me it just adds unnecessary complexity to something as simple as mapping six properties to another set of objects.
If you want to get more concrete help with code examples, you'll have to give us some more context. Why does a method named "GetValues" has a return type of void? I have a hard time coding up an example with that in mind. :)

Databinding to the DataGridView (Enums + Collections)

I'm after a little help with the techniques to use for Databinding. It's been quite a while since I used any proper data binding and want to try and do something with the DataGridView. I'm trying to configure as much as possible so that I can simply designed the DatagridView through the form editor, and then use a custom class that exposes all my information.
The sort of information I've got is as follows:
public class Result
{
public String Name { get; set; }
public Boolean PK { get; set; }
public MyEnum EnumValue { get; set; }
public IList<ResultInfos> { get; set; }
}
public class ResultInfos { get; set; }
{
public class Name { get; set; }
public Int Value { get; set; }
public override String ToString() { return Name + " : " Value.ToString(); }
}
I can bind to the simple information without any problem. I want to bind to the EnumValue with a DataGridViewComboBoxColumn, but when I set the DataPropertyName I get exceptions saying the enum values aren't valid.
Then comes the ResultInfo collection. Currently I can't figure out how to bind to this and display my items, again really I want this to be a combobox, where the 1st Item is selected. Anyone any suggestions on what I'm doing wrong?
Thanks
Before you bind your data to the grid, first set the DataGridViewComboBoxColumn.DataSource like this...
combo.DataSource = Enum.GetValues(typeof(YourEnum));
I generally do this in the constructor after InitializeComponent(). Once this is set up you will not get an exception from the combo column when you bind your data. You can set DataGridViewComboBoxColumn.DataPropertyName at design time as normal.
The reason you get an exception when binding without this step is that the cell tries to select the value from the list that matches the value on the item. Since there are no values in the list... it throws an exception.

Resources