I wrote myself a library to help with generic database lookups. When I use it I get a class that has all properties blank. However, the base class is properly filled. The product variable is properly filled. How I can make the code fill the derived class (TModel entity)? When I set breakpoint in the dataservice Create method (comment inside code) these are the results in Locals/Autos window:
public class GenericLookupModelDataService<TModel, TViewModel> : object, IDisposable
where TModel : GenericLookupModel, new()
where TViewModel : GenericLookupViewModel, new()
public virtual void Create(TViewModel product, string username = "SYSTEM")
{
TModel entity = new TModel
{
is_active = product.Active,
value = product.Name,
created_on = product.CreatedOn,
created_by = product.CreatedBy,
modified_on = product.ModifiedOn,
modified_by = product.ModifiedBy
};
if (string.IsNullOrEmpty(entity.created_by)) //breakpoint here
entity.SetCreated(username);
if (string.IsNullOrEmpty(entity.modified_by))
entity.SetModified(username);
_db.Set<TModel>().Add(entity);
_db.SaveChanges();
}
TViewModel based on GenericLookupViewModel class:
public abstract class GenericLookupViewModel
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(300)]
public string Name { get; set; }
[Required]
public bool Active { get; set; }
[StringLength(50)]
[DisplayName("Record last modified by")]
public string ModifiedBy { get; set; }
[DisplayName("Record last modified Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime ModifiedOn { get; set; }
[StringLength(50)]
[DisplayName("Record created by")]
public string CreatedBy { get; set; }
[DisplayName("Record creation Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime CreatedOn { get; set; }
}
TModel based on GenericLookupModel class:
public abstract class GenericLookupModel : IActive, ICreated, IModified, IIdentity, IStringValue
{
public bool is_active { get; set; }
public string value { get; set; }
public DateTime created_on { get; set; }
public string created_by { get; set; }
public DateTime modified_on { get; set; }
public string modified_by { get; set; }
public int id {get;set;}
public void SetCreated(string creator = "SYSTEM")
{
created_by = creator;
created_on = DateTime.Now;
}
public void SetModified(string modifier = "SYSTEM")
{
modified_by = modifier;
modified_on = DateTime.Now;
}
public void ToggleActive()
{
is_active = !is_active;
}
}
Controller and Action:
public class PrimaryFocusController : GenericLookupViewModelController<PrimaryFocusViewModel,tblkp_PrimaryFocus>
{
public override ActionResult Create(PrimaryFocusViewModel lookup)
{
SetBrowsingUser(AppUser.Login);
return base.Create(lookup);
}
}
When I compile the library I get warning messages which might have to do something on this:
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.id' hides inherited member 'MyLib.Model.GenericLookupModel.id'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.value' hides inherited member 'MyLib.Model.GenericLookupModel.value'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.is_active' hides inherited member 'MyLib.Model.GenericLookupModel.is_active'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.created_on' hides inherited member 'MyLib.Model.GenericLookupModel.created_on'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.created_by' hides inherited member 'MyLib.Model.GenericLookupModel.created_by'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.modified_on' hides inherited member 'MyLib.Model.GenericLookupModel.modified_on'. Use the new keyword if hiding was intended.
warning CS0108: 'DataLayer.tblkp_PrimaryFocus.modified_by' hides inherited member 'MyLib.Model.GenericLookupModel.modified_by'. Use the new keyword if hiding was intended.
The DataLayer.tblkp_PrimaryFocus is a class generated from EF using DB First approach.
UPDATE: User #code4life brought a good point - to have all those properties of the child classes marked as virtual (tblkp_PrimaryFocus), but that would mean that I would need to mark all of them each time model is regenerated from EF Diagram - that's what I'm trying to avoid - modifying the EF-generated classes.
I know it is late to the game, but I stumbled in here struggling with some of the same issues. The first point for anyone to get into the code is to follow msdn. The main take-away to get started is add debug="true to the top line in the .tt file, e.g., <## template language="C#" debug="false" hostspecific="true"#> becomes <## template language="C#" debug="true" hostspecific="true"#>. Now you can debug the code by right clicking the .tt files.
The place I think the questionnaire was referring is to simpleProperties in the .tt file. Here you can add override or other keywords if need be :)
You can modify the T4 templates (.tt files) to mark them as overriding or new as appropriate. The properties on the base type would have to be marked virtual of course.
I believe overriding would work as the behavior you observe seems to be due to your Create method not being able to access the inherited properties (it only has the definitions of the base, and they're not virtual). That said, without getting too deep into your design I imagine this is not exactly what you want.
What you'd really want is to prevent the code generator from emitting the properties on the derived class to begin with so that all callers use the base definitions. I suggest you first look into the T4 templates again to see if you can't add rules that match against properties of your base type. Alternatively, consider custom attributes to match them.
It feels like there's a general way to solve this issue, so consider formulating a report for the EF team if you can confirm (I haven't thought about it very much).
Expect recommendations to switch to a Code-First approach however. Frankly, I'm tempted to recommend it to you myself.
Related
I have a simple class like this.
public class Greeting
{
public string From { get; set; }
public string To { get; set; }
public string Message { get; set; }
}
Strangely I get the following warning.
Severity Code Description Project File Line Suppression State
Warning CS8618 Non-nullable property 'From' must contain a non-null value when exiting constructor.
Consider declaring the property as nullable. MxWork.Elsa2Wf.Tuts.BasicActivities
D:\work\MxWork\Elsa2.0WfLearning\MxWork.Elsa2.0Wf.Tuts\src
\MxWork.Elsa2Wf.Tuts.BasicActivities\Messages\Greeting.cs 5 Active
I am baffled. These new kind of messages that it throws pulls down my confidence.
I got them from all the three properties.
And this has suddenly appeared.
Can some one please suggest how this can be mitigated.
Update
These days I have seen using default! like so, and its working.
public class Greeting
{
public string From { get; set; } = default!;
public string To { get; set; } = default!;
public string Message { get; set; } = default!;
}
Also you may put a question mark symbol(?) to indicate that the type is nullable, if you feel appropriate as follows.
public class Greeting
{
public string? From { get; set; };
public string? To { get; set; };
public string? Message { get; set; };
}
If you don't want this, you can disable this by deleting the below line from the csproj file or setting it as disable. By default value is disable.
<Nullable>enable</Nullable>
Here is the official documentation.
The compiler is warning you that the default assignment of your string property (which is null) doesn't match its stated type (which is non-null string).
This is emitted when nullable reference types are switched on, which changes all reference types to be non-null, unless stated otherwise with a ?.
For example, your code could be changed to
public class Greeting
{
public string? From { get; set; }
public string? To { get; set; }
public string? Message { get; set; }
}
to declare the properties as nullable strings, or you could give the properties defaults in-line or in the constructor:
public class Greeting
{
public string From { get; set; } = string.Empty;
public string To { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty;
}
if you wish to retain the properties' types as non-null.
You can annotate a property directly as non-nullable.
public string Property{ get; set; } = null!;
And it will give a warning if user tries to set the Property as null
For Entity Framework Working with nullable reference types:
public class NullableReferenceTypesContext : DbContext {
public DbSet<Customer> Customers => Set<Customer>();
public DbSet<Order> Orders => Set<Order>();
}
You can also implement a constructor to remove the error.
public class Greeting
{
public string From { get; set; }
public string To { get; set; }
public string Message { get; set; }
public Greeting(string from, string to, string message)
{
From = from;
To = to;
Message = message;
}
}
Having nullable reference types turned on will save you a lot of heartaches when it comes to running your application. The problem with a lot of warnings is that most may not cause a problem, but it may hide that one that is causing a hard-to-find bug.
There are a few gotchas in using it like the question points out and answer very well by Slate and others.
It is a very good idea to have as near as possible to zero warning.
With nullable enabled, it produces a lot of warnings. Many times the compiler just knows something could be null. However, you being smarter than the compiler you know that by the time it reaches that code it won't be null.
For example:
public partial class Exams: ComponentBase
{
[Inject] private IQuestionPoolFetchService? QPoolService { get; init; }
private async Task FetchQuestionPool()
{
await QPoolService.GetAllQuestionsFromText();
}
This will throw a CS8602 warning. Because maybe somehow the DI will send a null. Of course, we know that isn't going to happen.
You could get rid of the warning with #prama like:
public partial class Exams: ComponentBase
{
[Inject] private IQuestionPoolFetchService? QPoolService { get; init; }
private async Task FetchQuestionPool()
{
#pragma warning disables CS8602 // Dereference of a possibly null reference.
await QPoolService.GetAllQuestionsFromText();
#pragma warning restore CS8602 // Dereference of a possibly null reference.
}
This is very ugly code and gets worst if you have to repeat it many times.
A better solution:
Using the null-forgiving operator. "!"
public partial class Exams: ComponentBase
{
[Inject] private IQuestionPoolFetchService? QPoolService { get; init; }
private async Task FetchQuestionPool()
{
await QPoolService!.GetAllQuestionsFromText();
// null-forgiving ^
}
This tells the compiler hey, I know this could be null, but it won't be.
Click on Project Name -> Open .csproj file
Replace <Nullable>enable</Nullable>
With <Nullable>disable</Nullable>
In Visual Studio 2022 v17.2.3
Visual Studio Project Settings
Right click your Project title > Properties > Application > Packaging > Under the "Build" section > Set "Nullable" = "Enable" in the dropdown.
When I searched for this question, specifically for Blazor WebAssembly [WASM], I found an alternative to Darryl Wagoner WA1GON's answer.
Using [DisallowNull] which is a precondition guard according to Microsoft's docs on Attributes for null-state static analysis interpreted by the C# compiler. It is well worth reading about more in-depth.
public partial class UploadView : FluxorComponent
{
private static readonly Serilog.ILogger logger = Serilog.Log.ForContext<UploadView>();
[Parameter][DisallowNull] public string? UploadHeader { get; set; }
[Parameter][DisallowNull] public EventCallback<SaveFile>? OnUploaded { get; set; }
[Inject][DisallowNull] private IState<UploadViewState>? State { get; set; }
[Inject][DisallowNull] private IDispatcher? Dispatcher { get; set; }
// NOTE: I included this as an example of trying to use the null-forgiveness / old school approach
public UploadViewState Model {
get {
if (State is null) { logger.Warning("{#Component}.Model - State is null", GetType().Name); }
if (State?.Value is null) { logger.Warning("{#Component}.Model - State.Value is null", GetType().Name); }
return State!.Value!;
}
}
protected override void OnInitialized()
{
base.OnInitialized();
Dispatcher.Dispatch(new Initialized());
Dispatcher.Dispatch(new UploadFileSizeChanged(524288000));
logger.Debug("{#Component} - Initialized", GetType().Name);
}
// yada yada...
}
NOTE: If this seems like a lot of work for you to maintain you can use this Fody Weaver to have it done for you.
Just put:
#nullable disable
at the top of a Razor Page
Since C#11 (.NET 7.0) you can add a required modifier.
public class Greeting
{
public required string From { get; set; }
public required string To { get; set; }
public required string Message { get; set; }
}
This feature aims to improve how we initialize objects that do not rely on constructor parameters so it won't feel like a cheat.
But with the required modifier, you won't be able to use var greeting = new Greeting(); to create an object instance. Now to create an object instance you must do something like:
var greeting = new Greeting()
{
"Me",
"You",
"Some msg..",
};
Originally answer was posted on:
What is best way to create .NET6 class with many non-nullable properties?
More detailed info:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required
If you are using an entity framework, your nullable/non-nullable attribute should align with your database, which will be either NULL or NOT NULL; whether it be SQL, json, etc.. This is just my opinion, but I prefer consistency over some warning at the build level. In other words, if your database has data that is non-nullable, your code's data structure for importing that data should also be non-nullable, vice versa.
When working with a list of String you can use:
public List<string> ? nameOfPublicConstructor { get; set; }
I did not find any answer on the forum (however if there is one please let me know). I am writing backend structure for the ASP.NET MVC app and have some troubles with C# case.
I am wondering if such a solution is possible to happen (and need to do a following thing the way I described or similarly).
I will show the problem in the example below, because I do not know how to say it clearly in words.
I have defined Enum as following:
public enum myEnum
{
FirstOpt = 0,
SecondOpt= 1,
ThirdOpt = 2,
Unknown = -1,
}
Now, I want to assign myEnum in the different part of my solution in the custom attribute.
The attribute looks the following way:
public class SearchTypeAttribute : Attribute
{
public Type SType { get; set; }
public IEnumerable<object> SItems { get; set; }
}
Then I want to use it in the following way:
public class SomeClass
{
[Key]
public long Id { get; set; }
[SearchTypeAttribute(
SType = typeof(Enums.myEnum),
SItems = Enum.GetValues(typeof(Enums.myEnum))
)]
public string Type{ get; set; } // later to this item will be assigned string name of the assigned value
}
When I am doing this, the following error appears:
Error CS0655 'SItems' is not a valid named attribute argument because it is not a valid attribute parameter type
I was also trying to assign it as here:
public class SomeClass
{
[Key]
public long Id { get; set; }
[SearchTypeAttribute(
SType = typeof(Enums.myEnum),
SItems = Enums.myEnum // here !
)]
public string Type{ get; set; }
}
But I still have no idea what "Type" of property I should use in my SearchTypeAttribute, to be able to assign those values there.
I am doing this to be able to generate different types of fields in search bars in the views later.
Then in my code I want to assign the list of enum values or the specific enum to some variable, so I can then, operate on those values.
What types should I use to assign this type of data SItems ?
Is there other approach to do it ?
I am not yet really advanced in c#. Thank you for any help in advance.
An attribute is a compile-time thing. So you have to provide all the information at compile-time also. However Enum.GetValues will be executed at runtime, making it impossible to be used for an attribute. The only way to achieve this is by writing the possible enum-values directy into the attribute:
public class SomeClass
{
[Key]
public long Id { get; set; }
[SearchTypeAttribute(
SType = typeof(Enums.myEnum),
SItems = new[] { Enums.FirstOpt, Enums.SecondOpt, ...}
)]
public string Type{ get; set; }
}
Apart from this I can´t see why your SItems is of type IEnumerable<object>, when it obviously has only Enums-elements in it. It´s not even possible to use an IEnumerable on an attribute, only arrays of primitive types are allowed, as mentioned here. So SItems should be an Enums[].
Another approach is to rely on the attributes constructor and initialize SItems from there:
public class SearchTypeAttribute : Attribute
{
public SearchTypeAttribute(Type type)
{
this.SType = type;
this.SItems = Enum.GetValues(type);
}
}
Now simply use the attribute as follows:
[SearchTypeAttribute(typeof(Enums.myEnum))]
public string Type{ get; set; }
i rare use EF. so i have a question. if we do not use virtual keyword with dbset then lazy load will not work?
i read a article from this link https://stackoverflow.com/a/24114284/5314244
they provide code like
public class AppContext : DbContext
{
public AppContext()
{
Configuration.LazyLoadingEnabled = true;
}
public virtual DbSet<AccountType> AccountTypes { get; set; }
}
public class AccountType
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<AccountCode> AccountCodes { get; set; }
}
public class AccountCode
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid AccountTypeId { get; set; }
public virtual AccountType AccountType { get; set; }
}
they said :
The virtual keyword on the navigation properties are used to enable lazy loading mechanism, but the LazyLoadingEnabled property of the configuration must be enabled.
The virtual keyword on AccountType::AccountCodes navigation property will load all account codes the moment there is a programmatically access to that property while the db context are still alive
if we declare this code without virtual keyword public DbSet<AccountType> AccountTypes { get; set; } then when this line execute var accountCodes = accountType.AccountCodes; what will happen?
Error will thrown or null will be stored in accountCodes variable?
second question what is default in EF---> lazy load or eager loading? how many type of loading option available ?
thanks
The approach you want to achieve, lazy loading, means that the data provider, in your case Entity Framework, retrieves certain data at the time you need it. If, for example, you access all account types you probably don't need to access all account codes for these account types thus retrieving them from the database would be unnecessary.
If you had to write the SQL yourself, you would most likely write a plain select statement without joining the child relations:
SELECT * FROM AccountTypes;
Another use-case on the other hand might need access to this data so you would either end up with eagerly loading it which results in a join operation...
SELECT * FROM AccountTypes JOIN AccountCodes ON AccountCodes.AccountTypeId = AccountTypes.Id;
...or to rely on Entity Framework's lazy loading ability.
The latter case requires Entity Framework to do the following: First, selecting all account types and then, at the time your user-code accesses the account codes, emitting another select statement to retrieve the codes for the respective account type. To do so, Entity Framework needs a hook, i.e. it must know when you access the navigation property, which is in general impossible because this would need additional code in the getter of this property:
public class AccountType
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<AccountCode> AccountCodes
{
get { /* Load child data here */ }
set { /* ... */ }
}
}
Unfortunately Entity Framework is unable to alter the POCO class because it's your user-code, but what it can do is deriving from your class and overriding the property to inject it's magic. For this to work it creates what is called a proxy, i.e. it creates a class at runtime which derives from your POCO class, like:
public class AccountTypeProxy : AccountType
{
public override ICollection<AccountCode> AccountCodes
{
get { /* Load child data here */ }
set { /* ... */ }
}
}
So what you get when you retrieve the account types is actually a bunch of proxy instances, which you can confirm during debugging.
If you would not add the virtual keyword to the navigation property or if you sealed the class, Entity Framework could not override the property or could not derive from the class at all and thus lazy loading would not work.
I'm facing query a weird issue using the latest version of Entity Framework, regarding navigation properties.
I do have an entity of which I have a few required navigation properties which are marked as virtual.
See my entities class below:
public class Folder : UserReferencedEntityBase<int>
{
#region Constructors
public Folder()
{ }
public Folder(IUnitOfWork unitOfWork)
: base(unitOfWork)
{
ParentFolder = unitOfWork.Context.GetCurrentFolder as Folder;
}
#endregion
#region Properties
[Required]
public string Name { get; set; }
[Required]
public string Data { get; set; }
[Column(Order = 998)]
public Folder ParentFolder { get; set; }
[Required]
public bool IsPublished { get; set; }
#endregion
}
This one is inheriting from UserReferencedEntityBase{T} which looks like:
public class UserReferencedEntityBase<TKey> : EntityBase<TKey>
{
#region Constructors
public UserReferencedEntityBase() { }
public UserReferencedEntityBase(IUnitOfWork unitOfWork)
{
unitOfWork.ThrowIfNull("unitOfWork");
CreatedBy = unitOfWork.Context.GetCurrentUser;
}
#endregion
#region Properties
[Required]
[Column(Order = 996)]
public virtual IdentityUser CreatedBy { get; set; }
[Column(Order = 997)]
public virtual IdentityUser UpdatedBy { get; set; }
#endregion
}
Now, I do have my MVC website where I'm loading an entity, updating a property and saving it in the database again:
var model = new FolderManager(UnitOfWork).GetFolder(id);
model.IsPublished = true;
UnitOfWork.Commit();
I use a custom Unit Of Work here, but no rocket sience at all. Everything is happening with the same context, within the same request, no async calls, ...
When I do execute the code, I receive:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
Looking at this, reveals the following error:
"The CreatedBy field is required."
Now, the weird this is, when I'm debugging my code, the 3 lines given above, the created_by property is filled in and the code does execute without any problem.
I'm using ASP.NET identity Framework, thus using an IdentityDbContext in case that matters.
Anybody has a clue?
Kind regards
UPDATE - Folder Manager
The manager is just a wrapper to get my contents out of my unit of work:
public Folder GetFolder(int id)
{
return UnitOfWork.FolderRepository.GetByFilter(x => x.Id == id);
}
The GetByFilter method is constructed like:
public virtual TEntity GetByFilter(Func<TEntity, bool> filter)
{
DbSet.ThrowIfNull("DbSet");
if (OnBeforeEntityGet != null)
{ OnBeforeEntityGet(this, new RepositoryEventArgs(typeof(TEntity))); }
if (OnEntityGet != null)
{ OnEntityGet(this, new RepositoryEventArgs(typeof(TEntity))); }
return !Entities.Any() ? null : !Entities.Where(filter).Any() ? null : Entities.First(filter);
}
Just want to let you know that I've found a solution.
It seems that when you're loading an entity that contains virtual properties but never inspecting them, they stay null and therefore the code is not working.
With the debugger is attached, it's working flaslessy after inspecting this element.
Is this normal behaviour?
Kind regards,
Let's say I created a few models via Entity Framework, and one of them is called Paper_Results. This is how the class might look:
public partial class Paper_Results
{
public string Color { get; set; }
public int Height { get; set; }
public int Width { get; set; }
}
I want to use this class like a domain model. Now let's say I create a class the derives from Paper_Results with an added interface
public class Construction_Paper : Paper_Results, IMeasurementType
{
[Required]
public (new?) string Color { get; set; }
[Required]
[Range(1, Int32.MaxValue, ErrorMessage = "Value should be greater than or equal to 1")]
public (new?) int Height { get; set; }
[Required]
[Range(1, Int32.MaxValue, ErrorMessage = "Value should be greater than or equal to 1")]
public (new?) int Width { get; set; }
public virtual string MeasurementType
{
get { return "inches"; }
}
}
Now when I create my ViewModel, I'll used the derived class instead:
public class Construction_Paper_ViewModel
{
Construction_Paper cp;
List<Construction_Paper> cpList;
string title;
public Construction_Paper_ViewModel()
{
title = "Construction Paper";
cp = new Construction_Paper();
cpList = new List<Construction_Paper>();
}
}
I know I should be using uint instead of int for non-negative integers, but I just wanted to add more data annotations to the code. What I'm asking is what is the best OOP technique to derive from Paper_Result class, so that I don't have to modify it at all. The reason is because if I create a new solution and/or project, I don't want to do any modifications to it when I auto-regenerate it using Entity Framework. Should I use shadowing? Or the new keyword in the derived class? Or do you guys have any other better ideas?
The auto-generated EF models do not contain 'virtual' in their methods, thus the reason why I brought up shadowing and the new keyword.
First of all, not every problem should be addressed via inheritance.
Second, the .NET framework already has a mechanism to add metadata (attributes) to an existing object. These are called Buddy Classes, and use the MetadataTypeAttribute class.
The idea is that you add an attribute to the class that allows you to specify a different class that is used to define the metadata for the original class. It's not pretty, but it gets the job done.