I want to give a field (or property) an alternative name that can be shown in the user interface by using reflection. I have found the attribute DescriptionAttribute, but is it really for this purpose or am I better off using something else?
Is this attribute somehow restricted to Windows Forms and its property view, or is it UI framework independent? (currently I am on Windows Forms for the project, but it might change in the future so I don't want to be stuck with it)
public class MyCustomZoo
{
[Description("Cute Mouse")]
public MyCustomAnimal CuteMouse;
[Description("Frightning Lion")]
public MyCustomAnimal FrightningLion;
}
I have found my preferred solution in one of the answers here.
using System.ComponentModel.DataAnnotations;
// ...
public class MyCustomZoo
{
[Display(Name = "Cute Mouse")]
public object CuteMouse;
[Display(Name = "Frightning Lion")]
public int FrightningLion;
}
public static string FieldDisplayName(FieldInfo field)
{
DisplayAttribute da = (DisplayAttribute)(field.GetCustomAttributes(typeof(DisplayAttribute), false)[0]);
return da.Name;
}
// ...
// c# identifier names, results in {"CuteMouse","FrightningLion"}
List<string> fieldNames = typeof(MyCustomZoo).GetFields().Select(field => field.Name).ToList();
// "human readable" names, results in {"Cute Mouse","Frightning Lion"}
List<string> fieldDisplayNames = typeof(MyCustomZoo).GetFields().Select(field => FieldDisplayName(field)).ToList();
Don't forget to add a reference to assembly System.ComponentModel.DataAnnotations.
Note: System.ComponentModel.DisplayNameAttribute could be used as well (thanks to Kieran Devlin) if properties is what you want to tag. With pure fields it doesn't work however.
Related
I'm quite new in using Reporting Services, and I'm currently facing this problem:
I have two report files (.rdlc). One of them was made by one partner of mine in my entreprise, the other by me. I want to Add a new Dataset to my report, so I can use it in a Tablix. The Data source is correct, but in the Available datasets dropdown, the object I want (Indicator4Report) is not present 1:
If I press the New... button, and then the Object button, I can find the object I want to add as dataset 2:
But clicking it shows this error: object reference not set to an instance of an object 4
The object is declared in the file Foo.Weee.Logic.ViewModels.Advisor, which looks like this:
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Collections.Generic;
using System.Linq;
namespace Foo.Weee.Logic.ViewModels.Advisor
{
public class ResultsSummary
{
public string blabla {get;set;}
...more attributes...
}
...more objects...
public class ResultsSummary4Report
{
public ResultsSummary4Report(ResultsSummary results)
{
this.blabla = results.blabla;
...more code...
this.indicatorBlabla = new Indicator4Report[] //The object I want
{
new Indicator4Report() { title = "Profesional Independiente", value = 7.5f , color1 = "red", color2 = "gray" }
};
...more code...
}
public string blabla {get;set;}
...more attributes...
public Indicator4Report[] indicatorBlabla {get;set;} //The object I want
...more attributes...
}
...more objects...
/* The object I want */
public class Indicator4Report
{
public string title { get; set; }
public float value { get; set; }
public string color1 { get; set; }
public string color2 { get; set; }
}
}
As I said, I'm new to this, so maybe it is a dumb mistake or something I'm doing wrong, but I don't know how to tackle the problem.
Thank you very much for your help, and if you need more info, code or screenshots, feel free to ask.
PS: I'm using Visual Studio 2015
Ok, I found the problem.
As I have the Reports and the Foo.Weee.Logic.Advisorfile in different projects, I had to add a link to that file in the Report project (Right button -> Add -> Existing Item... -> Add As Link).
After doing that, I could happily use the object I wanted as Data source without errors.
For me, it turns out all the projects have be built with Any CPU or it won't work. If you force x64, the designer can't read that and it crashes! Also, make sure all the classes are marked as [Serializable] or you might have problems, too.
i got the same problem in my rdlc report. what i did to solve this is just simply deleted my dataset and add the same dataset of my report. this problem occured in my situation due to some modification in web.config file. hope it will help you
I am trying to handle multiple languages in an ASP.NET Webforms (.NET 4.5, C#) application of mine.
Basically, some of my entities in my SQL Server 2012 database have properties like Name or Description which exist in three languages - German, French, Italian.
Those are stored as columns Name_De (German), Name_Fr (French), and Name_It (Italian).
When I create my .NET objects from the database, of course, I also get those three properties in my entity class. But for displaying on screen, in a grid for instance, it would be nice if I could somehow "magically" always show the "right" language. This should be based on the Thread.Current.CurrentUICulture.TwoLetterISOLanguageName (which returns de, fr or it, depending on the browser's language preferences).
So I was hoping to somehow be able to create e.g. a .NET attribute that would allow me to do something like this:
Base "Module" entity - generated from existing SQL Server database:
public partial class Module
{
public string ModuleCode { get; set; }
public string Name_De { get; set; }
public string Name_Fr { get; set; }
public string Name_It { get; set; }
... // other properties
}
Partial extension in a separate file
public partial class Module
{
[Multilingual]
public string Name { get; set; }
}
The base idea is: I can access the Module.Name property, and depending on the current setting of CurrentUICulture, either the value of Name_De, Name_Fr or Name_It would be fetched, when I access the getter of the Name property.
Can something like this be done in C# ? I have looked at a lot of custom attribute implementations, but nothing ever seemed to be doing something like this...
Assuming you are using two separate entities (one generated by your SQL entities and one "business entity" which only contains a Name property), are you open to using something like AutoMapper ?
If you are, then you could tweak your resolve function to map the entity depending on the current thread culture.
switch(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant())
{
case "DE":
return dto.Name_De;
case "FR":
return dto.Name_Fr;
// ...
default :
return String.Empty;
}
which would work for your scenario.
If this is a solution that could work for you, I think this question is very close to what you're looking for : Automapper Mapping for Localization Resolver in a Multi-Language Website
If you do go down the custom attribute route, you will have to deal with Reflection stuff and string parsing I'm afraid. AFAIK, there is no built in way to do this with the localization functions provided by .NET. AutoMapper will hide that from you.
The problem with custom attributes in this case is that you are still trying to access the Name property. You are trying to "shadow" the default behaviour of the property by making it access other properties. If I understand correctly you want the Multilingual custom attribute to turn your property into :
public String Name
{
get
{ switch(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant())
{
case "DE":
return dto.Name_De;
case "FR":
return dto.Name_Fr;
// ...
default :
return String.Empty;
}
}
}
If that's correct, then you won't be able to do that easily with attributes, simply because the attribute will never be aware of the existence of the Name_De property.
Other option that still isn't quite what you're looking for :
void Main()
{
Module mod = new Module();
mod.Name_De = "name";
mod.Name_Fr = "nom";
// This is the unfortunate nasty bit. I address the property using its name
// in a String which is just bad. I don't think there is a way
// you will be able to address the ".Name" property directly and have
// it return the localized value through your custom attribute though
String localizedValue = mod.GetLocalizedProperty("Name");
}
[AttributeUsage(AttributeTargets.Property)]
public sealed class MultilingualAttribute : Attribute
{
public MultilingualAttribute()
{
}
}
public static class ModuleExtensions
{
public static String GetLocalizedProperty(this Module module, String propName)
{
var type = typeof(Module);
var propInfo = type.GetProperty(propName);
// Make sure the prop really is "Multilingual"
if(Attribute.IsDefined(propInfo, typeof(MultilingualAttribute)))
{
String localizedPropName = propInfo.Name;
switch(Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName.ToUpperInvariant())
{
case "DE":
localizedPropName += "_De";
return type.GetProperty(localizedPropName).GetValue(module, null).ToString();
case "FR":
localizedPropName += "_Fr";
return type.GetProperty(localizedPropName).GetValue(module, null).ToString();
}
}
return String.Empty;
}
}
public class Module
{
public String Name_De {get; set;}
public String Name_Fr {get; set;}
[Multilingual]
public String Name {get; set;}
public Module()
{
}
}
I don't know of a more powerful way to use custom attributes for what you're looking for unfortunately. Quite frankly, I don't think this is a good solution, only posted because I was trying to see what I could do with custom attributes. There is no real point in using this code over a more "normal" property which would do the same thing in a clearer way (without attributes). As you say in your original question, your goal is to intercept the call to the Name property and this doesn't achieve it.
In a ASP.NET MVC 5 web site I have a GridView using the devexpress component binding using the LINQ method.
EF generated a partial class to map a table that i use to display in that gridview.
In this partial class generated by the EF i have a ID_Status property wich has a corresponding description in other table. I made another partial class to deal with this custom Property and it works ok, except when i try to make a 'Sort' operation clicking on the header of this column.
The partial class generated by the EF.
[Table("Test")]
public partial class Test
{
[Key]
public long ID_Test { get; set; }
public long ID_TestStatus { get; set; }
//other properties
}
My Custom partial class:
public partial class Test
{
private static readonly TestRepository _testRepository;
static TestRepository()
{
_testRepository= new TestRepository();
}
public string StatusDescription
{
get { return _testRepository.GetStatusDescriptionById(ID_TestStatus); }
}
}
When i try to Sort using another column it works fine, but when i try to Sort using the custom property Column all the grid cell values gets empty, without any value.
Any suggestion?
It's not a very good idea to have data access code inside an entity. One reason is that it makes it very hard to write unit test. Another reason is that it is very likely to give rise to the n + 1 anti pattern. In your case, it does: one (1) query to get the Tests, then each Test (n) sends a separate query to the database to get its StatusDescription.
The way you implemented it also raises some eyebrows, because
_testRepository is static, which meas there is probable some context instance living for the entire lifecycle of the application - unless GetStatusDescriptionById creates a new context for each call, but that wouldn't be a good idea either.
The GetStatusDescriptionById call is made each time the property is accessed. In a web application this may not be a big problem because the objects are newly created each time they are requested anyway, but in other environments this could be highly inefficient.
A better approach would be to fetch the Testss with their Status included:
context.Tests.Include(t => t.TestStatus)
and have an unmapped property like
public string StatusDescription
{
get { return TestStatus== null ? string.Empty : TestStatus.Description; }
}
better still (in my opinion) would be not to show Test objects directly, but TestDto objects like
public class TestDto
{
public string StatusDescription { get; set; }
//other properties that match Test's properties
}
and use a tool like AutoMapper to map a collection of Tests to TestDtos. If Test has a property Status and TestStatus has a property Description, AutoMapper will be able to flatten that into StatusDescription automatically.
Both this StatusDescription property and the Dto appraoch set the state of a Test(Dto) object once. I don't think any grid component can mess with that.
I'm still not yet sure on the best way to store selectlist options for front end display or db storage.
I've been using Enums at the moment, and also using description decorators (How do you create a dropdownlist from an enum in ASP.NET MVC?)
I'm now thinking that I might as well just create a full class for this stuff, so I can store the following information properly with full control:
Item Name
Full description
int for storage in db
order
Any methods to get information in anyway from the list.
Is it right I should be thinking about implementing all this myself by hand? I want a really solid way of doing this, and an enum doesn't really feel like it's going to cut it.
Is it right I should be thinking about implementing all this myself by
hand?
Yes. Enums are often leaky and insufficient abstractions that aren't always suitable for the complex domain model you actually wish to represent.
Rather than roll your own, you may want to consider Headspring's Enumeration class (via github, nuget). We use it all the time instead of enums because it's nearly as simple and is much more flexible.
An example of a "State" enumeration and using it as a select list:
public class State : Enumeration<State>
{
public static State Alabama = new State(1, "AL", "Alabama");
public static State Alaska = new State(2, "AK", "Alaska");
// .. many more
public static State Wyoming = new State(3, "WY", "Wyoming");
public State(int value, string displayName, string description) : base(value, displayName)
{
Description = description;
}
public string Description { get; private set; }
}
public IEnumerable<SelectListItem> Creating_a_select_list(State selected)
{
return State.GetAll().Select(
x => new SelectListItem
{
Selected = x == selected,
Text = x.Description,
Value = x.Value.ToString()
});
}
I'm not trying to sell you on this particular implementation, you could certainly hand code your own (the Enumeration class is only about 100 lines of code). But I definitely think you'd benefit from moving beyond basic enums. It is the right approach given the scenario you described in your question.
The first place where such information shoiuld be is the database...or any "virtual store" such as a web service that offers an interface to you db. In fact if there are other db entiies that use these values THEY MUST be represented in the database, otherwise you will run in big troubles. In fact, suppose one of such values is a string....if you don't define a table containing all possible values+a key and simply write the string as it is in other tables...it will be impossible for you to change the format of the string since it will be "spread" all over your db...On the contrary, if you just use an external key to refer to such strings...you can easily change them since the string is stored in just ONE place in your db.
Also the enumeration solution suffers of the problem that you cannot add or deleted values...so if such operations "conceptually" might make sense you cannot use an enumeration. You can use enumeration when all options "conceptually span" all possibilities, so you are sure you will never add/delete other options, such as in the case of the enumeration (yes, no, unknown).
That said, once you have your options in the db the remainder is easy...you will have DTO entities or Business entities representing them in exactly the same way you do for all other DB entities.
For visualization purposes you may have a ViewModel version of this options that might just contain key and description, and a "Repository method" that your controllers can call to have the list of all options.
Once retrieved you controllers put them in the overall page ViewViewModel...together with all other information to be shown on the page. From the ViewModel...you can access them to put them in a dropdown.
Summing up:
1) You need a DB representation of your options
2) Then you will have DTO, business layer, and View versions of this entities...as needed, exactly as for all other DB entities.
Are you looking for a one-size-fits-all solution for all your select list options? I personally advocate choosing the option that best fits the specific issue.
In a recent project I was introduced to a hybrid of a Smart Enum. Here's an example (I apologize for typos, I'm typing this cold):
public class Priority
{
public enum Types
{
High,
Medium,
Low
}
public Types Type { get; private set; }
public string Name { get { return this.Type.ToString(); } } // ToString() with no arguments is not deprecated
public string Description { get; private set; }
public static High = new Priority{ Type = Types.High, Description = "..."};
public static Medium = new Priority{ Type = Types.Medium, Description = "..."};
public static Low = new Priority{ Type = Types.Low, Description = "..."};
public static IEnumerable<Priority> All = new[]{High, Medium, Low};
public static Priority For(Types priorityType)
{
return All.Single(x => x.Type == priorityType);
}
}
So, in implementation, you could store the Enum value, but you would reference the object itself (Priority.For(entity.priority)) for the additional metadata when rendering your views.
Is that closer to what you're looking for?
Of course, one of the gotchas is if you need to write a query against the database that relies on the metadata on the lookup, this solution is going to create a few tears along the way.
You can use "repository pattern" for data access and use viewmodels between your controllers and views. Example:
//Model
public class CustomerViewModel
{
public Customer customer { get;set; }
public IEnumerable<Village> Villages { get; set; }
}
//Controller
public ActionResult Index()
{
var customerViewModel = new CustomerViewModel
{
Customer = new Customer(),
Villages = _villageService.GetAll()
};
return View(customerViewModel);
}
//View
#model ViewModel.RegisterViewModel
#Html.DropDownListFor(q => q.Customer.VillageId, new SelectList(Model.Villages, "Id", "Title"), "Please Select")
I have written a blog post about repository pattern, you may have a look.
I store my options in the View Models themselves:
public class ViewModel {
[Required]
public int SelectListValue { get; set; }
public IDictionary<String,String> SelectListOptions {
get {
return new Dictionary<String, String>{
{ "0", Resources.Option1},
{ "1", Resources.Option2},
{ "2", Resources.Option3}
};
}
}
}
Then I can just drop the following line into my view to render the select list:
<%= Html.DropDownListFor(m => m.SelectListValue, new SelectList(this.Model.SelectListOptions, "Key", "Value", "")) %>
I am using C# to create a view model that I later serialize into Json for use with KnockoutJs.
Now I need to add information on a property level if a certain user has access to view and/or edit the property.
Since Javascript has little to no reflection possibilities I want to do this with C# before serializing to Json.
My view model could look like this:
class ViewModel {
public string Name { get; set; }
}
I want to use a User Access Service that would take the user, which has some Roles, and the view model and go through the Roles and apply the access rights. Something like this:
class AccessService {
public void Apply(IUser user, ViewModel viewModel) {
if(user.Roles.Contains(Roles.Admin)) {
viewModel.AllowRead(vm => vm.Name);
}
}
}
The viewModel.AllowRead(..) method would be an extension method that would take an object (or maybe an interface or type if necessary) and this is where the magic would happen.
I would like the result of this operation to be that the viewModel would get a new property with the name CanRead which in turn would have a boolean property with the name Name.
The resulting viewModel would look like this:
class ViewModel {
public string Name { get; set; }
public object CanRead { // Could non anonymous as well.
return new {
Name = true;
};
}
}
Can this be done with dynamics or do I have to use Reflection.Emit? I'm not asking for "show me the codez". I just want to know if my idea is mental or if it's possible.
Any ideas?
I think it would be possible and you could use the Lambdanator to help achieve this.
Example usage:
Lambda.Reflect<SomeClass>(x => x.AMethod());
Lambda.Reflect<SomeClass>(x => x.AProperty);
Lambda.Reflect(() => local);