Get field by name in Entity Framework - c#

What would be the easiest and least labour intensive (from the software POV) for me to be able to get a property (to be read and modified) from an entity generated via the Entity Framework?
Example (and simplified code) below:
// <auto-generated> from Entity Framework
public partial class tblCustomer
{
public int CustomerID { get; set; }
public string Status { get; set; }
}
For instance I would like:
tblCustomer customer = new tblCustomer();
int pCustomerID = customer.GetFieldByName("CustomerID");
pCustomerID = 100;
I've read a lot of answers about Reflection, but also comments that it's heavy processing (may be a bit excessive for my needs).
My example code may seem rather simplistic (so a solution would be rather pointless for that, the real code is based on large tables), but if there was some sort of GetFieldByName function I could reduce my code significantly as I have a lot of repetitious code doing the same stuff (just to different columns).

If I understand your problem correctly, I think you can use the changetracker for this (if the entity is in the context already).
dbContext.Entry(customer).CurrentValues["CustomerID"]
will give you the value of CustomerID for the customer object, provided it is attached to the dbContext instance.
If it is not part of the context, you can use Attach() to attach it first, or use Add(), if it's supposed to be a new record.

If you don't like to use Reflection the only way that i know is using a dictionary in your entities and also you can put all these stuff in a base class and your entities inherit it for example like that:
[Serializable]
public class BaseEntity
{
Dictionary<string, object> _dic;
public BaseEntity()
{
_dic = new Dictionary<string, object>();
}
public object this[string propertyName]
{
get
{
return _dic[propertyName];
}
set
{
_dic[propertyName] = value;
}
}
}
public class tblCustomer : BaseEntity
{
public int CustomerID
{
get
{
return (int)this["CustomerID"];
}
set
{
this["CustomerID"] = value;
}
}
public string Status
{
get
{
return (string)this["Status"];
}
set
{
this["Status"] = value;
}
}
}
tblCustomer customer = new tblCustomer();
int pCustomerID = customer["CustomerID"];
and about performance cost of Reflection you can for first time store your memberInfos in a static field and use it for all instances.

Related

EF Core: use a dictionary property

Is there a way to fill a dictionary property with Entity Framework Core?
For performance reasons, we like to search in the application instead of the database. As a list won’t scale well, we like to use a dictionary.
For example (simplified example)
class Course
{
public Dictionary<string, Person> Persons { get; set; }
public int Id { get; set; }
}
class Person
{
public string Firstname { get; set; }
public string Lastname { get; set; }
}
Things I tried
Naively just add a dictionary property. This will result the in following error:
System.InvalidOperationException: The property 'Persons' could not be mapped, because it is of type 'Dictionary' which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Try adding a value conversion (with HasConversion), but conversion one only works on a single item and not on collections. The HasMany already gives a compile error:
builder
.HasMany<Person>(c => c.Persons) //won't compile, Persons isn't a IEnumerable<Person>
.WithOne().HasForeignKey("PersonId");
Creating a custom collection class (inherited from Collection<T> and implement InsertItem, SetItem etc.) – unfortunately this also won’t work because EF Core will add the item to the collection and first after that will fill the properties (at least with our OwnsOne properties, that is not in the demo case) - SetItem won't be called afterwards.
Adding a "computed" property that will build the dictionary, the setter won't be called (the list is updated every time with partly values, a bit the same as above). See try:
class Course
{
private Dictionary<string, Person> _personsDict;
public List<Person> Persons
{
get => _personsDict.Values.ToList();
set => _personsDict = value.ToDictionary(p => p.Firstname, p => p); //never called
}
public int Id { get; set; }
}
Of course I could build a dictionary in the Repository (using the Repository pattern), but that’s tricky as I could forget some parts – and I really prefer compile time errors over run-time errors and declarative style over imperative style code.
Update, to be clear
this isn't a code first approach
the idea to change the mapping in EF Core, so no database changes. - I haven't tagged the database on purpose ;)
If I use a List instead of Dictionary, the mapping works
It's a 1:n or n:m relationship in the database (see HasMany - WithOne)
I don't think saving a dictionary is a good idea (I can't even image how it would be done in the database). As I can see from you source code you are using the FirstName as key. In my opinion you should change the dictionary to a HashSet. This way you can keep the speed but also save it to the database.
Here is an example:
class Course
{
public Course() {
this.People = new HashSet<Person>();
}
public ISet<Person> People { get; set; }
public int Id { get; set; }
}
After this you can create a dictionary from it, or keep using the hashset. Sample for dictionary:
private Dictionary<string, Person> peopleDictionary = null;
public Dictionary<string, Person> PeopleDictionary {
get {
if (this.peopleDictionary == null) {
this.peopleDictionary = this.People.ToDictionary(_ => _.FirstName, _ => _);
}
return this.peopleDictionary;
}
}
Please note that this would mean that your People Set becomes unsynced after you add/remove to/from the dictionary. In order to have the changes in sync you should overwrite the SaveChanges method in your context, like this:
public override int SaveChanges() {
this.SyncPeople();
return base.SaveChanges();
}
public override int SaveChanges(bool acceptAllChangesOnSuccess) {
this.SyncPeople();
return base.SaveChanges(acceptAllChangesOnSuccess);
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) {
this.SyncPeople();
return base.SaveChangesAsync(cancellationToken);
}
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) {
this.SyncPeople();
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}
private void SyncPeople() {
foreach(var entry in this.ChangeTracker.Entries().Where(_ = >_.State == EntityState.Added || _.State == EntityState.Modified)) {
if (entry.Entity is Course course) {
course.People = course.PeopleDictionary.Values.ToHashSet();
}
}
}
EDIT: In order to have a running code, you will need to tell the EF not to map the dictionary, via the NotMapped Attribute.
[NotMapped]
public Dictionary<string, Person> PeopleDictionary { ... }
Seems someone has been struggling with that and found solution. See: Store a Dictionary as a JSON string using EF Core 2.1
The definition of the entity is as follows:
public class PublishSource
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
}
In the OnModelCreating method of the database context I just call HasConversion, which does the serialization and deserialization of the dictionary:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<PublishSource>()
.Property(b => b.Properties)
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => JsonConvert.DeserializeObject<Dictionary<string, string>>(v));
}
One important thing I have noticed, however, is that when updating the entity and changing items in the dictionary, the EF change tracking does not pick up on the fact that the dictionary was updated, so you will need to explicitly call the Update method on the DbSet<> to set the entity to modified in the change tracker.
You could add a new property PersonsJson for storing the JSON data. It automatically serializes or deserializes the JSON data into the Persons property when data is retrieved from DB or stored to DB. Persons property is not mapped, only PersonsJson is mapped.
class Course
{
[NotMapped]
public Dictionary<string, Person> Persons { get; set; }
public string PersonsJson
{
get => JsonConvert.SerializeObject(Persons);
set => Persons = JsonConvert.DeserializeObject<Dictionary<string, Person>>(value);
}
public int Id { get; set; }
}
Create a partial class of the type generated by EF.
Create a wrapper class that holds a dictionary or implement IDictionary.
Implement the Add function so it also adds the value to the list that EF uses.
The first time a method that operates on the Persons list or dictionary is called make sure they are properly initialized
You would end up with something like:
private class PersonsDictionary
{
private delegate Person PersonAddedDelegate;
private event PersonAddedDelegate PersonAddedEvent; // there can be other events needed too, eg PersonDictionarySetEvent
private Dictionary<string, Person> dict = ...
...
public void Add(string key, Person value)
{
if(dict.ContainsKey(key))
{
// .. do custom logic, if updating/replacing make sure to either update the fields or remove/re-add the item so the one in the list has the current values
} else {
dict.Add(key, value);
PersonAddedEvent?.(value); // your partial class that holds this object can hook into this event to add it its list
}
}
// ... add other methods and logic
}
public partial class Person
{
[NotMapped]
private Dictionary<string, Person> _personsDict
[NotMapped]
public PersonsDictionary<string, Person> PersonsDict
{
get
{
if(_personsDict == null) _personsDict = Persons.ToDictionary(x => x.FirstName, x => x); // or call method that takes list of Persons
return _personsDict;
}
set
{
// delete all from Persons
// add all to Persons from dictionary
}
}
}
public List<Person> Persons; // possibly auto-generated by entity framework and located in another .cs file
if your going to access the list of Persons directly then you need to also modify your partial class so that adding to the list will add to the dictionary (perhaps using a wrapper for the Persons list or a wrapper class all together)
there are some improvements to be made if dealing with large data sets or needing optimization, eg not deleting/re-adding all elements when setting new dictionary
you might need to implement other events and custom logic depending on your requirements
i don't know if this would solve the problem or not, but when i tried to run your provided code. it triggered a runtime error that required me to modify the Persons property declaration to like like this
public Dictionary<string, Person> Persons { get; set; } = new Dictionary<string, Person>();
this eliminated the runtime error and every thing went fine.

How can I create a generic implementation that lists data from my database?

I've got a large number of tables in my database that are essentially supporting data. These tables list nationalities, genders, languages, etc. and are all based on the same data model:
public class SupportDataModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Deleted { get; set; }
}
public class Gender : SupportDataModel
{
}
This data is presented in DropDownList controls mostly so I need to query each table to get a list. Since I don't want to have to rewrite this query every time I need to access the data, I've written it as a helper class:
public class GendersHelper : IAlternateHelper<Gender>
{
public List<Gender> ListItems()
{
using (var db = new ApplicationDbContext())
{
return db.Genders.Where(x => !x.Deleted).ToList();
}
}
}
For each of these classes, this function is identical except in the table it queries. That's why I'd like to write a single class that uses the type that I pass in to it as the determining factor for which table I'm querying, but I don't know how to do this.
Here's what I've got so far...
public abstract class SupportingDataHelper<T>
{
public List<T> ListItems()
{
// Logic to determine which table gets queried,
// as well as the query itself should go here.
}
}
How do I get this method to determine from the type passed in which table to query and then return a list of those items?
You can just use DbContext.Set<T> which returns a set for selected type:
public class SupportDataRepository<T> where T : SupportDataModel
{
public List<T> ListItems()
{
using (var db = new ApplicationDbContext())
{
return db.Set<T>().Where(x => !x.Deleted).ToList();
}
}
}
However, I wouldn't call this class Helper, because it looks more like a repository.
Another thing to consider is that you definitely don't want to create an empty class like:
public class Gender : SupportDataModel
{
}
because it doesn't make much sense. Perhaps, you may want to use enum property to define a type of SupportDataModel. In this case, you will have only one table (with more rows though), one simple class with simple repository class and no inheritance or generics.

Let class have unique id which doesn't ever change, no matter what

I have classes that might or might not change their name (and members) during development. My classes are used (in most cases) like enums, but I couldn't use enums because I needed slightly more functionality. Since classes (obviously) don't have an Integer representing them under the surface I need to create some solution for having similar functionality. In other words, I want for each class to be represented by an Integer (or some other unique identifier).
I've created this attribute:
public class IdAttribute : Attribute
{
private int id = -1;
public IdAttribute(int index)
{
this.id = index;
}
public int Id
{
get
{
return id;
}
}
}
And I'm using it as following:
[Id(0)]
public class Hello: Core { }
[Id(1)]
public class Bye: Core { }
As you can see it's quite error prone, since I don't want any class to have the same Id. And thus, optimally I want an automatic generated id, but I don't want it to change if I ever change anything regarding the class, for example the class name or its members.
What's the best way to achieve this?
(I know that in Java, that once you make a class Serializable, you'll get an automatically generated id (is there something like this in C#?).)
EDIT:
The reason I "couldn't" just use enums is because of (mainly) convenience. I have classes which exposes fields in an editor. And in this editor I can select only the appropriate "enums", in some cases only enums which inherits from "Core" will be displayed and in other cases they might inherit from "Tools" or some other class. I hope that cleared up a bit.
Not sure why you'd need to do this, but you could do the following:
[AttributeUsage(AttributeTargets.Class)]
public class IdAttribute:Attribute
{
public Guid Id { get; }
public IdAttribute(string id)
{
Id = new Guid(id);
}
}
And you'd use it like:
[IdAttribute("7d7952d1-86df-4e2e-b040-fed335aad775")]
public class SomeClass
{
//example, you'd obviously cache this
public Guid Id => GetType().GetCustomAttribute<IdAttribute>().Id;
//...
}
Do note, that Guids are not random. If you need a random id, then this isn't the solution. To generate a Guid read comments to your question.
You can handle that through your base class Core:
public abstract class Core
{
public Core()
{
Type myType = this.GetType();
object[] attrs = myType.GetCustomAttributes(typeof(IdAttribute), false);
IdAttribute attr = attrs?.OfType<IdAttribute>().FirstOrDefault();
int id = -1;
if (attr != null) id = attr.Id;
if (!reservedIdentities.ContainsKey(id))
{
reservedIdentities.Add(id, myType);
}
else
{
if (!reservedIdentities[id].Equals(myType))
throw new ArgumentException("Duplicate identities discovered.", nameof(id));
}
}
static Dictionary<int, Type> reservedIdentities = new Dictionary<int, Type>();
//...
}

How can I clone a class with all the values that are already set in it?

class example:
public class Customer
{
public int CustomerID { get; set; }
}
using the class:
Customer customer1 = new Customer();
customer1.CustomerID = 1;
Now how can I create a customer2 class with all the values that are stored in customer1?
You can do it manually:
var customer2 = new Customer { CustomerID = customer1.CustomerID };
You can implement ICloneable interface in Customer class:
public class Customer : ICloneable
{
private int CustomerID { get; set; }
public Customer Clone()
{
return new Customer { CustomerID = this.CustomerID };
}
object ICloneable.Clone()
{
return this.Clone();
}
}
and then use it:
var customer2 = customer1.Clone();
You can serialize your object into XML/JSON and then deserialize it into new object, as described in this answer: Deep cloning objects in C#.
Or you can use reflection to get and copy all properties/fields values into your new Customer instance. It could have bad performance, but you'd have to measure it to make sure how bad it is.
Edit
One more way to do that: you can make reflection version faster using Expression Tree! Get all fields/properties and compile all necessary assignments at runtime using Expression.Lambda. After that every next Clone call will use compiled code so there will be no performance drawback at all. I've created Clone<T> extension method which does exactly that, using Expression class, static constructor and reflection. You can find the code on CodePlex: CloneExtension.cs
Either you use reflection to copy the values or you would want deep clone (ICloneable).
To extend Marcin's answer, if all of your items in your class are value types or immutable types (int, double, string, ect.) you can just use MemberwiseClone(). This will create a shallow copy of the original object, and if all your members are immutable there is no need to do a deep copy. This can be useful if you have many objects in your class you need to copy over.
public sealed class Customer : ICloneable
{
private int CustomerID { get; set; }
public Customer Clone()
{
return (customer)this.MemberwiseClone();
}
object ICloneable.Clone()
{
return this.Clone();
}
}
What if you set the class up like so:
public class Customer
{
private Customer Parent {get; private set;}
private int _id;
public int CustomerID
{
get { return Parent == null ? _id : Parent.CustomerID; }
set
{
if(Parent != null)
throw new InvalidOperationException("...");
_id = value;
}
public Customer()
{
}
public static Customer Clone(Customer parent)
{
return new Customer{Parent = parent};
}
}
This would create a immutable clone. Now... if you need to be able to alter the values... then either take a different approach, or expand on it.

Is it possible to have a List<string> as a property on an active record class

Is it possible to have a HasMany relationship of a basic type such as String, on an ActiveRecord class, without the need for creating another entity such as (TodoListItem) to hold the value.
[ActiveRecord]
public class TodoList
{
[PrimaryKey]
public int Id
{
get { return _id; }
set { _id = value; }
}
[HasMany(typeof(string)]
public IList<string> Items
{
get { return _items; }
set { _items= value; }
}
}
Can anyone help?
Yes, you can do this. You can map a one-to-many relation to a built-in or simple type (value type or string) rather than a persisted type.
You'll need to specify the ColumnKey, Table and Element params in the HasMany attribute declaration to get it to wire up properly. You have to have a surrogate key column so the AR can handle updates and cascades, and then Element tells AR which column in the table holds the simple value it will use to make the list.
[HasMany(typeof(string), Table="ToDoList_Items",
ColumnKey = "ListItemID", Element = "Item")]
public IList<string> Items { get; set; }
(or something similar - I haven't got a compiler handy on this box to check it; but per the API docs it ought to work.)
Speaking of which, if you haven't already had a look, http://api.castleproject.org is kinda indispensible for any work with the Castle stack.
In ActiveRecord, your types map to a record in a table (by default). It seems like you are confusing how this type should map to your table.
The MyClass type should have a definition something like this (excluding the PK settings):
[ActiveRecord(Table = "MyTable")]
public class MyClass : ActiveRecordBase<MyClass>
{
[Property]
public int Id { get; set; }
[Property]
public int MyClassId { get; set; }
[Property]
public string ListItem { get; set; }
}
Then, to load the list:
public void LoadMyClasses()
{
MyClass[] results = MyClass.FindAll();
}
I'd suggest you spend some time with the ActiveRecord documentation (or tutorial) as that should also help clear up any confusion.

Categories