Reputation: 1626
I'm looking for ways to circumvent the restrictions on attribute parameters when creating a custom attribute. Specifically, I have a custom DisplayName attribute that goes and retrieves data from the database and then determines what to display in the LabelFor helper on the view. This is part of a larger prototype, so code refinement is a secondary concern right now. We'll be implementing caching and other performance refinements when we're ready to start developing the actual project. I can provide some context for my actual problem, but I'm hoping answers will be general enough to apply to any type of attribute that needs to do this.
The constructor doesn't do any work besides assigning parameters to some private properties:
public DisplayNameTranslationAttribute(string pageName = "", [CallerMemberName] string fieldName = "")
{
_pageName = pageName;
_fieldName = fieldName;
}
All of the heavy lifting occurs in the DisplayName override method. The reason for this is that DisplayName is called every time the page loads, as opposed to the constructor which is called only once when the page is first loaded and then never again.
public override string DisplayName
{
get
{
string displayName = string.Empty;
_customerID = 1; //This needs be accessed or retrieved somehow, from somewhere
using (var db = new SomeDataEntities())
{
SomeData sd =
db.SomeDatas.FirstOrDefault(t => t.PageName == _pageName && t.FieldName == _fieldName);
SomeOtherData od = null;
if (sd != null)
{
od = sd.SomeOtherDatas.FirstOrDefault(c => c.CustomerID == _customerID);
displayName = Equals(od, null) ? sd.ColumnN : od.ColumnZ;
}
}
return displayName.IsNullOrWhiteSpace() ? _displayName : displayName;
}
}
As you can see, I'm right now hard-coding _customerID to 1. I need to be able to provide an actual value to _customerID. I know that I can't pass it as a parameter directly to the attribute, so what I need to do is discover methods to circumvent this restriction. Are there any options to trick the attribute or, more likely, retrieve the ID from somewhere else in memory?
Upvotes: 1
Views: 105
Reputation: 35106
I never could imagine Display attribute could be miss-used like that. Please stop doing that!
If label name is taken from DB, then it is likely part of the domain knowledge and should not be treated like a view logic (which DisplayName
attribute is).
Instead of having to do these hacks, just get this information with ViewModel that is passed from controller to the view. And controller will take the data from DB. Simple!
// pseudo-code
public ActionResult Indes(int customerId)
{
var viewModel = new ViewModel();
viewModel.Value = dbContext.Customers.FirstOrDefault(c => c.CustomerId == customerId);
viewModel.DisplayName = dbContext.FieldNames.FirstOrDefault(f => f.Name == some condition);
return View(viewModel);
}
EDIT: As a second thought, you can try create your own attribute, completely separate from Display Attribute, place there what field should place there. And then implement DataAnnotationsModelMetadataProvider that will be giving you correct field names for labels.
See sample implementation of this Metadata Provider in my little app. This is shamelessly stolen from Jimmy Bogard. This implementation does not do what you need, but shows a sample.
Upvotes: 3