I have built a data driven model by dragging my tables onto the empty designer of an edml. I know that I can establish a datacontext around my model, and that I can access each table as though it were a class, but where is the code for each of these models?
I ask because I want to add validation to the setters of each property on the class itself.
Look for a .dbml file on the root level of your project. Under that file, you will see a file called <someDB>.designer.cs.
However, (and you'll see this at the top of the designer file as well), you don't really want to make any changes to the classes in this file.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
If you want to make additions, you should look into creating Partial classes that extend the functionality of the generated classes.
Here is a sample partial class that I created to handle my validations:
namespace ETL_Framework_UI
{
[MetadataType(typeof(DataObjectMD))]
public partial class DATA_OBJECT:IValidatableObject
{
public class DataObjectMD
{
[Required(ErrorMessage="The object name is required")]
[StringLength(50, ErrorMessage = "Name cannot exceed 50 characters")]
public string OBJECT_NAME { get; set; }
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
ETLDbDataContext db = new ETLDbDataContext();
var field = new[] { "OBJECT_NAME" };
var param = db.DATA_OBJECTs.SingleOrDefault(r => r.OBJECT_NAME == OBJECT_NAME && r.OBJECT_ID != OBJECT_ID);
if (param != null)
{
yield return new ValidationResult("Object name is already in use. ", field);
}
NOTE Your partial class must be defined in the same namespace that you see at the top of your designer.cs file.
You do not want to edit the auto-generated classes themselves, this will lead to huge problems if you ever need to generate them again. You want to use partial classes with DataAnnotations instead. This way your validation/extra rules will be forever separated and easier to maintain.
Entity framework + validation annotation
If you are going to add validation maybe Entity Framework Code First is a better solution.
Related
I have a UI designer on the front end which creates a layout.
A layout has rows, and each row has columns, and each column has widgets. The widgets are identified by a key and they also have a config.
public class Layout
{
[Required]
public IEnumerable<Row>? Rows { get; init; }
}
public record Row
{
[Required]
public IEnumerable<Column>? Columns { get; init; }
}
public record Column
{
[Required]
public IEnumerable<Widget>? Widgets { get; init; }
}
public record Widget
{
[Required]
public string? WidgetTypeKey { get; init; }
public object? Config { get; init; }
}
The config of a widget could be any number of C# classes that don't share anything in common. I don't know which one it will be until I determine the widget type by key. So therefore I have used object as the type.
But the config classes still have validation requirements such as [Required], [Range], [MaxLength] and so on.
I can resolve the config class at run time, but I'm not sure how to go about this so that I still get all of the usual ASP.NET validation through the pipeline.
At first I thought I could attach [BindModel] to the Config property or the Widget class and use my own IModelBinder, but these aren't used at all. ASP.NET only considers them if they're at the top of the hierarchy. I.e. the layout. So the model binder is never hit.
I also tried writing an IModelBinderProvider, but again the same problem. The provider is only hit for the initial Layout type, but nothing beyond that. It never queries again for any other type.
I also experimented with generics, thinking that maybe Config could be a TConfig type, but I have no idea how to resolve that at runtime during model binding. Especially since each widget can be a different type.
I guess I could write my own model binder for a layout, but then I miss out on all the automated validation don't I?
Is this too crazy to attempt? Has anyone ever successfully resolved a dynamic object at runtime with a deeply-nested complex type while also letting ASP.NET core do its full validation pipeline?
Your Layout class should implement IValidatableObject interface with Validator class:
public class Layout : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
...
Validator.TryValidate(...);
...
}
}
You should have the validation logic with System.ComponentModel namespace, ASP.NET Core will pick it up automatically.
I'm moving from mainly classic asp to .NET. So this may be a stupid question, but I can't find an answer.
I have an MVC App using Database First and Entity Framework. Now I would like to add some logic to the auto generated 'partial' classes. From what I have read it should be a matter of creating a new partial class with the same namespace and name. But when I do that I get an error "(This member is defined more than once)" and "Ambiguity between [partial class] and [partial class]". I understand what the error is saying, but I'm not sure how to resolve the problem.
I would like to add some logic to the set; accessor.
So in the generated class I have
public partial class QualityChecks
{
.....
public int DailyCount { get; set; }
...
}
in my new partial class I would like to add to the set code to make sure only values greater then 0 are added. If a negative value is added it needs to be logged and changed to 0
e.g. my new partial class is:
public partial class QualityChecks {
public int DailyCount {
set
{
DailyCount = value;
if it's < 0 log and set to 0
}
}
If that's not clear maybe this will help:
Currently I have loads of code that simply does
QualityChecks qc = new QualityChecks();
qc.DailyCount = enteredAmount;
....
db.QualityChecks.add(qc);
Rather then update that logic everywhere it would be nice to have it wrapped up in the QualityChecks class.
Is this the right way of going about it? If so what do I need to change to make this work?
Thank you in advance for any tips and help!
You cannot define the same members in two different files.
You can try to define a new wrapper property (eg. MyDailyCount) that add that extra logic and update the underlying DailyCount at the end so it get persisted to database.
public int MyDailyCount
{
get { return DailyCount; }
set
{
DailyCount = value;
// your extra logic
}
}
I have a model that stores company information, including tax IDs. In the US, these are 9 digit numbers and are typically displayed as ##-#######. However, in my system, I am storing these as strings with no hyphen - since other countries can have identification numbers that differ in length and format, I don't want be limited to a US standard.
Now I want to program my views to display US tax IDs in their "friendly" format. I have this working right now with a helper method I put in the Company model class:
public string FormatTaxID(string TaxID)
{
if (Address.Country == "United States")
return Regex.Replace(TaxID, #"(\d{2})(\d{7})", "$1-$2");
else
return TaxID;
}
Then in my view, I'm using:
#item.FormatTaxID(item.TaxID)
This all works fine, but it doesn't feel right to store a method like this in the model - it feels like this is more of a view/view model responsibility than a model responsibility, as it is solely for presentation.
I am using view models and thought of putting it there, but I I have multiple view models for the underlying model and don't want to repeat code if I don't have to. Also, my view model for the index uses collections and I'm not sure how I would work the method into it:
public class CompanyIndexViewModel
{
public IEnumerable<Company> Companies { get; set; }
public IEnumerable<Document> Documents { get; set; }
}
How would I apply this method to a collection like that?
Another option is creating a new helper/utility class and sticking it in there. What would MVC convention dictate?
For one-offs, I'd say use the view model. If it's something that you will reuse over and over, move it into a utility class that your views/view models/etc. can reference.
And, there's technically nothing wrong sort of doing it both ways. Put the method in a utility class and then add a property to your view model that returns this, e.g.:
public class CompanyIndexViewModel
{
...
public string TaxID { get; set; }
public string USFormattedTaxID
{
get { return Utilities.FormatTaxID(TaxID); }
}
}
The localized-to-the-Company-context TaxID of the company is properly a property of the Company, and is not a presentation detail.
I have an application (mvc 3) where the code is autogenerated for the datacontext class. And I need to expand the functionality, so I created partial class with the same name. But I found that only "void methods" could be marked as partial, while I need kind of a partial property.
So, is there any way to expand property's functionality in C#?
Updated:
Here is the code:
public Table<Post> Posts
{
get
{
// writing info into Trace file
Log = Console.Out;
var result = this.GetTable<Post>();
Log = new LogLinqToSql();
SubmitChanges();
return result;
}
}
The thing is that if I make any change to the data model this code will disappear, so how can I move it to the "safer" place?
Sorry for using the answer field, the post is for the discussion above:
Can't you just wrap the property from the DataContext in another property in your class
e.g.
partial class NewClass
{
public Table<Post> NewProperty
{
get
{
DoHouseKeeping();
return this.PropertyFromOtherPartialClass;
}
}
}
Only the classes need to be marked as partial.
The solution for my problem (provide logging) is simple: In the autogenerated code there is a bunch of partial helper methods. And there is a method "OnCreated" which is called when the instance of MyTypeDataContext class is created. So, I just need to do the following:
public partial class WebStoreDataContext
{
partial void OnCreated()
{
// writing info into Trace file
Log = Console.Out;
Log = new LogLinqToSql();
SubmitChanges();
}
}
If You want to add Attributes to the properties this post provides all the information)
I've currently got an issue where I need to see which fields have been changed on an Edit field for auditing purposes, in which I have code for, but I think my problem lies within my MVC View.
I have (test code):
[HttpPost]
public ActionResult Adjustment(GroupPolicy groupPolicy)
{
if (ModelState.IsValid)
{
_service.SaveGroupPolicy(groupPolicy);
return RedirectToAction("Index");
}
return View(groupPolicy);
}
Which is fine, the Policy saves. However, take this into consideration:
GroupPolicy has, say, 3 fields (in reality there are, maybe, 60):
bool IsPolicy
string Name
string Description
Name and Description are on the form, so that's fine. IsPolicy isn't used on the form, so that gets defaulted to false when posted back to the GroupPolicy object in the Adjustment method.
I can't really put IsPolicy in a Hidden field on the form, as that won't be elegant for 60+ fields in my actual solution, the HTML would be all over the place.
Now that the bool is defaulted to false, it completely abolishes the chance of me knowing if the field has changed or not. All I really want is a method for this data to be preserved, whilst keeping the new information on the Edit form.
Is this possible, am I missing something obvious?
Well first of all, GroupPolicy should be a view model and not an entity - and as such it should be tailored for the view e.g.
public class GroupPolicyViewModel
{
[HiddenInput]
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
...
}
Then in your action you don't need to worry about assigning values that have changed, you just map the view model directly across e.g.
public ActionList Adjustment(GroupPolicyViewModel viewModel)
{
if (ModelState.IsValid)
{
// pull actual entity from service
var groupPolicy = _service.GetGroupPolicy(viewModel.Id);
// update entity from view model
groupPolicy.Name = viewModel.Name;
groupPolicy.Description = viewModel.Description;
...
}
}
This keeps a clean separation between your view & business logic. Also, it allows you to add annotations for client-side validation without affecting your real model.
GroupPolicy has, say, 3 fields (in reality there are, maybe, 60)
I would recommend using AutoMapper for this e.g.
// call this once only e.g. Application_Start in the Global.asax
Mapper.CreateMap<GroupPolicyViewModel, GroupPolicy>();
...
// in your Adjustment action
var groupPolicy = _service.GetGroupPolicy(viewModel.Id);
groupPolicy = Mapper.Map<GroupPolicyViewModel, GroupPolicy>(viewModel, groupPolicy);
_service.SaveGroupPolicy(groupPolicy);
If IsPolicy not on the form then it shouldn't even be part of your model - this will prevent posting of this field into your model and so your check won't even be needed for IsPolicy.
Rather than accepting GroupPolicy as the parameter into the action, create a cut down object GroupPolicyInputModel with only fields that are on the form.
Then use your generic auditing to only compare all the posted fields, as per any other form.