In NHibernate they had a nifty little concept of a convention, where you could say, if an entity had a Name property, and it was a string, you could apply certain db behavior to it (for example, set it to not null, max length x, maybe unique if you could be certain all names would be unique).
It would be a small class, you'd add it to your instance factory, and bam! The db model would build accordingly.
Is there a comparable mechanism in EF Core? I can't seem to find any and the ridiculous number of fluent configurations for every entity for every property is quite tedious.
In EF Core the built-in conventions are used to create an initial model, which you can override or modify in OnModelCreating.
You can simply iterate over your entities and properties override the conventions. This works well enough in place of custom conventions from EF6 that (at least so far) custom conventions haven't made it off the backlog. See https://github.com/aspnet/EntityFrameworkCore/issues/214 for status and some examples. It's also a pretty common pattern to read a custom attribute in OnModelCreating to drive (or override) your entity configuration.
So your example:
if an entity had a Name property, and it was a string, you could apply certain db behavior to it (for example, set it to not null, max length x, maybe unique if you could be certain all names would be unique).
would be something like:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var props = from e in modelBuilder.Model.GetEntityTypes()
from p in e.GetProperties()
where p.Name == "Name"
&& p.PropertyInfo.PropertyType == typeof(string)
select p;
foreach (var p in props)
{
p.SetMaxLength(200);
p.IsNullable = false;
p.DeclaringEntityType.AddKey(p);
}
base.OnModelCreating(modelBuilder);
}
Or if you wanted to force the datatypes for all DataTime columns, something like:
var dateProps = from e in modelBuilder.Model.GetEntityTypes()
from p in e.GetProperties()
where p.PropertyInfo.PropertyType == typeof(DateTime)
select p;
foreach (var prop in dateProps)
{
prop.Relational().ColumnType = "datetime2(4)";
}
Related
I am using the ORM linq2db.
We have the need to set the column names at runtime.
That's why I can't use the attributes in the POCO-Class and instead use the Fluent Mapping Api (which is not documented as far as I know).
Because the column names will be determined at runtime I want to iterate over all properties in my Entities and set the Columnname.
In EF Core 3.x this would look like something like this:
foreach (var property in modelBuilder.Model.GetEntityTypes()
.SelectMany(t => t.GetProperties()))
{
property.SetColumnname(columnNameDict[property.Name]);
}
Is there a possibility to iterate over all the Properties in my Model, like in EF Core?
In linq2db to set the properties of a Property I have to use code like this:
builder
.Entity<Addresses>()
.HasTableName("ADDRESSES")
.Property(x => x.Company) // This is the Membergetter, which i can't use in a foreach afaik.
.HasColumnName("TEXT1")
To Access the Property ID I have to use "x => x.ID"(Expression<Func<Addresses, object>> memberGetter), but when I Iterate over the Properties, I don't know how to select the correct Property (PropertyMappingBuilder) without using this Expression (memberGetter) or how to set this Expression in a foreach. Is there a string overload like this .Property("Company")? I coudn't find any.
I tried it with reflection, but the Expression part didn't work ;):
foreach (var property in typeof(Addresses).GetProperties())
{
var fieldInfo = adressenInfo.GetFieldByUser(property.Name);
adressenBuilder.Property(a => property.GetValue(a)) // this line doesn't work
.HasColumnName(fieldInfo.LogicalName)
.HasDbType(fieldInfo.DataBaseType);
}
Any Ideas are welcome! Thanks :)
You need to generate proper LambdaExpression manually for Parameter() method.
var param = Expression.Parameter(typeof(Addresses));
adressenBuilder.Property(Expression.Lambda<Func<Addresses, object>>(
Expression.Convert(Expression.Field(param, fieldInfo), typeof(object)),
param))...
I have two large set of collection List<Class> with hundreds of properties.
e.g. Original Collection List<OriginalCollection> and Updated Collection List<UpdatedColleciton>
UpdatedCollection will contain value in certain columns which most probably will not be part of OriginalCollection and UpdatedCollection might have certain KeyColumn [ID Column] which might not be part of OriginalCollection, and I'm receiving thousands of data set in OriginalCollection and UpdatedColletion will increase in records over a period of time.
I do have a requirement where only null or empty column of OriginalCollection should get replaced with matching UpdatedCollection value by ID and if no matching ID is available then those records should get added in OriginalCollection from UpdatedCollection.
I tried with AutoMapper, where I tried to Update OriginalCollection with UpdatedCollection based on matching ID, for which I'm unable to find any AutoMapper configuration for my sets of above mentioned requirement.
I'm looking for most effective solution which should not impact on performance, thats why I did not go threw typical way of Union and Intersection, as Modal have hundreds of property and thousands of records are there, and as I do have plenty of properties I think library like AutoMapper would be good option than writing logic in loop to check value on each column for all thousands of record.
Please suggest any better and performance efficient solution like AutoMapper Configuration or any other .Net inbuilt feature to achieve this scenario.
I also checked with AutoMapper.Collection as below from https://github.com/AutoMapper/AutoMapper.Collection
cfg.CreateMap<OrderItemDTO, OrderItem>().EqualityComparison((odto, o) => odto.ID == o.ID);
Mapping OrderDTO back to Order will compare Order items list based on if their ID's match
Mapper.Map<List<OrderDTO>,List<Order>>(orderDtos, orders);
but it has below behavior and not working as expected to my requirement
If ID's match will map OrderDTO to Order
If OrderDTO exists and Order doesn't add to collection Not Working for me
If Order exists and OrderDTO doesn't remove from collection Not Working for me
AutoMapper is known mapping library and has good documentation as well, but unable to find similar detaild documentation for AutoMapper.Collection, I've explored AutoMapper.Collection but it was not providing solution as per my requirement.
So, I need to go traditional way.
Prepared differences of collection by LINQ query.
var common = original.Where(x => revised.Exists(z => z.ID == x.ID)).ToList();
var nonCommon = revised.Where(x => !original.Exists(z => z.ID == x.ID)).ToList();
foreach(var item in common)
{
var derived = revised.FirstOrDefault(x => x.ID == item.ID);
// Added Extention Method to compare and update property
var data = item.UpdateProperties(derived);
}
common.AddRange(nonCommon);
Utilized Reflection to compare objects and update it's value after comparision on property level, for all datatypes.
public static T UpdateProperties<T>(this T source, T destination)
{
Type type = source.GetType(); // Gets Source Object Type
PropertyInfo[] props = type.GetProperties(); // Gets Source object Properties
foreach (var prop in props) // Iterate threw all properties of source object
{
var sourceValue = prop.GetValue(source); // Get source object value by Property Name
var destinationValue = prop.GetValue(destination); // Get destination object value by Property Name, to update source object
// Update source object property value only if derived object's property has value and source object doesn't
if (string.IsNullOrEmpty(sourceValue?.ToString()) && !string.IsNullOrEmpty(destinationValue?.ToString()))
{
prop.SetValue(source, destinationValue); // Update source object's property with value of derived object
}
}
return source;
}
I have a large model which has been partially updated via deserialization. Since it has only been partially updated I would like to ignore any null values when I pass this to my entity framework update. Ultimately the EntityState.Modified is set but the issue I am having is that all fields are updated. This means anything that was null is now blanked in the database.
Is it possible to change this default behavior through a setting or override a method to check for null? It seems that since the context is expecting the full model I cannot simply set only a few values.
I've verified this by mapping only what I need to modify and the same behavior occurs.
You could implement something like this.
In this case I'm using a generic repository with reflection, to iterate through the properties and exclude null values in the update method.
public virtual TEntity Update(TEntity entity)
{
dbSet.Attach(entity);
dbContext.Entry(entity).State = EntityState.Modified;
var entry = dbContext.Entry(entity);
Type type = typeof(TEntity);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.GetValue(entity, null) == null)
{
entry.Property(property.Name).IsModified = false;
}
}
dbContext.SaveChanges();
return entity;
}
public IHttpActionResult PutProduct(int id, Product product)
{
NorthwindEntities db = new NorthwindEntities();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Products.Attach(product);
// Only the fields you want to update, will change.
db.Entry(product).Property(p => p.ProductName).IsModified = true;
db.Entry(product).Property(p => p.UnitPrice).IsModified = true;
db.Entry(product).Property(p => p.UnitsInStock).IsModified = true;
// only if if the value is not null, the field will change.
db.Entry(product).Property(p => p.UnitsOnOrder).IsModified =
product.UnitsOnOrder != null;
db.SaveChanges();
return Ok(product);
}
This is a somewhat tedious problem I've had to solve.
For lack of a more straightforward solution, eventually I decided I'd rather not want to try solving it as much downstream as when EF's SaveChanges-and-the-likes' get called (i.e., no need to "hook into" EF so late), but instead as much higher upstream / earlier on as possible --
that is, to do so right after I obtain a satisfying deserialization, which is meaningful to mutate the model, on a per-entity instance basis (in my use case, none of the updatable properties would represent relationships, but only independent attributes, in E/R parlance -- YMMV)
So, I opted for a "Populate" helper, along the lines of:
static void Populate(object from, object to)
{
var sourceType = from.GetType();
foreach (PropertyInfo target in to.GetType().GetProperties())
{
// Is the property at the target object writable and *not* marked
// as `[NotMapped]'?
var isUpdatable =
target.CanWrite &&
(target.GetCustomAttribute<NotMappedAttribute>(true) == null);
if (isUpdatable)
{
// If so, just find the corresp. property with the same name at the source object
// (caller is assumed responsible to guarantee that there is one, and of the same type, here)
var source = sourceType.GetProperty(target.Name);
var #default = sourceType.IsValueType ? Activator.CreateInstance(sourceType) : null;
var equality = (IEqualityComparer)typeof(EqualityComparer<>).MakeGenericType(sourceType).GetProperty("Default", BindingFlags.Public | BindingFlags.Static).GetValue(null);
var value = source.GetValue(from);
// Test for <property value> != default(<property type>)
// (as we don't want to lose information on the target because of a "null" (or "default(...)") coming from the source)
if (!equality.Equals(value, #default))
{
target.SetValue(to, value, null);
}
}
}
}
where "from" is the fresh entity instance that just got partially populated by whatever deserialization code, and where "to" is the actual target entity that lives in the DbContext (be it an EF proxy or not);
and where NotMappedAttribute is the EF's usual.
You'd typically have Populate to be called some time after the deserialization (&/or DTO-mapping) onto your "from" instance is done, but anyway before SaveChanges() gets call on your DbContext for all the "to" entities -- obviously, we assume there is a feasible 1-to-1 mapping "from" ... "to", that the caller of Populate knows about / could figure out.
Note I still don't know if there is a more elegant (more straightforward) way to do that, without recourse to reflection -- so, there, FWIW.
Remarks
1) above code can (or should) be made more defensive in various ways, depending on the caller assumptions;
2) one may want to cache those IEqualityComparer's (&/or the PropertyInfo's) for whatever (good) reason may arise -- in my case, I didn't have to;
3) finally, my understanding is that third-party librairies such as AutoMapper are also specially designed for that sort of task, if you can afford the additional dependency
'HTH,
I have a single instance of an object:
AS_SYSTEM system = ctx.AS_SYSTEM.Where(s => s.SYSTEM_ID == query).First();
And i want to remove some properties from it. All properties that ends with "Reference". Something like
system.GetType().GetProperties().Name.EndsWith("Reference")
I want to remove all ef properties that are linked to other tables.
To nullify (the values) of all properties ending with 'Reference', using reflection:
var properties = system.GetType().GetProperties().Where(x => x.Name.EndsWith("Reference"));
foreach (var p in properties)
{
p.SetValue(system, null, null);
}
Though I'm not sure that you really need to use reflection here, thats how its done.
What I'm trying to do is to pass an entity object to method and return all the names of the properties in it.
I'm using this code to get all the props names :
return classObject.GetType().GetProperties();
The problem is that this code return "EntityKey" and "EntityState" as properties whe I use it with Entity Object.
Is there any way to do it ?
Thanx in advance
You want all direct properties, but not the properties of the base type, which in your case is EntityObject:
var type = classObject.GetType();
//alternatively call out directly: typeof(EntityObject).GetProperties()...
var basePropertyNames = type.BaseType.GetProperties().Select(x => x.Name);
var props = type.GetProperties().Where(p => !basePropertyNames.Contains(p.Name));
This sample assumes there is a base type (which is the case for DB first), refactor when that is not guaranteed.
Edit from #Matt's comment: All of the above is unnecessary, could slap my head for not thinking of this - just use the right binding flags:
return classObject.GetType().GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
It is also possible without reflection:
using (var context = new ModelContainer())
{
// Access CSDL
var container = context.MetadataWorkspace
.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);
// Access name of related set exposed on your context
var set = container.BaseEntitySets[context.YourEntitySet.EntitySet.Name];
// Access all properties
var properties = set.ElementType.Members.Select(m => m.Name).ToList();
// Access only keys
var keys = set.ElementType.KeyMembers.Select(m => m.Name).ToList();
}
As you can see you have access to much more then names. The example shows that you can now which property is part of key. If you access Members directly you can know which property is scalar, complex type or navigation property.
All information are already loaded so there is no need for reflection. If you want to use reflection don't forget to use it only once (first time you need it) and then store and reuse received property names. Reflection is slow so using it each time you need names is a bad practice.
I had the same problem. The solution I found was to create an array with the name of the properties to return (I olnly need a few). In your case, since it can be laborious to keep track of all properties, I would filter the properties EntityKey and EntityState and return all the others. The code would be something like this:
public IEnumerable<PropertyInfo> GetProperties()
{
Type t = this.GetType();
return t.GetProperties()
.Where(p => (p.Name != "EntityKey" && p.Name != "EntityState"))
.Select(p => p).ToList();
}
Don't know if there is a better solution, but it would be nice ;) Hope it helps!
As stated by BrokenGlass but beware if you need performance and you want to do this in loops. Reflection is not a fast thing.
If you need performance you may wish to put a virtual method in your base class to retrieve the properties as an array of strings or whatever, and override that in all derived classes. This would be the fastes approach but with more coding.