Update Entity property in EF where property is another entity - c#

I am using Entity Framework 6 and I need to update the properties of a entity.
I have the following entities:
public class File
{
public Int32 Id { get; set; }
public Byte Data { get; set; }
public DateTime Updated { get; set; }
public virtual Mime Mime { get; set; }
}
public class Mime
{
public Int32 Id { get; set; }
public String Name { get; set; }
public virtual ICollection<File> Files { get; set; }
}
Then I used the following:
_repository.Update<File>(file, x => x.Data, x => x.Mime, x => x.Updated);
The repository method is the following:
public void Update<T>(T entity,
params Expression<Func<T, Object>>[] properties)
where T : class
{
_context.Set<T>().Attach(entity);
foreach (var property in properties)
{
MemberExpression expression =
property.Body is MemberExpression ?
(MemberExpression)property.Body :
(MemberExpression)(((UnaryExpression)property.Body)
.Operand);
_context.Entry<T>(entity)
.Property(expression.Member.Name).IsModified = true;
}
}
This works fine for Data and Updated properties but not for Mime. I get the error:
The property 'Mime' on type 'File' is not a primitive or complex property. The Property method can only be used with primitive or complex properties. Use the Reference or Collection method.
Is it possible to make this work and integrate it on my repository method?

Yes, I think that can be done. The problem here is that I didn't see any easy way to check whenever a property is part of the table, or is it navigational property. Thus it's hard to call the right behavior.
If you're interested, take a look at EF6 source code, InternalEntityEntry.cs -> Property(..) which does huge amount of property validation through metadata.
The main idea is to basically scan your conceptual model, and determine whenever the property is navigational property(eg if the property leads to another table), or if it's complex/primitive.
According to that, you call the right functionality.
var propertyName = expression.Member.Name;
var propertyType = __get_property_type__(propertyName);
if(propertyType==Property || propertyType==Complex)
{
_context.Entry<T>(entity)
.Property(propertyName).IsModified = true;
continue;
}
if(propertyType==Navigational){
// hm, do we need Attach it first?!
// not sure.. have to test first.
dynamic underlyingReference = entity.GetType()
.GetProperty(propertyName)
.GetValue(entity, null);
_context.Entry(underlyingReference).State = EntityState.Modified;
}
The catch here is to have __get_property_type__ that works. There's Microsoft.Data.Edm.dll that let's you work with the conceptual model, but it's not that easy I think.
This is the way how EF6 detects if we're dealing with reference property or not:
EdmMember member;
EdmEntityType.Members.TryGetValue(propertyName, false, out member);
var asNavProperty = member as NavigationProperty;
// if asNavProperty!=null, we have navigation property.

100% Gerts point. I see no reason to approach the problem they way you have.
Anyway, to answer the question. You have another answer there. Potentially useful.
Whats missing is this:
How to get a list of managed types from the context.
public static IList<Type> GetContextManagedTypes(DbContext context) {
ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext;
MetadataWorkspace workspace = objContext.MetadataWorkspace;
IEnumerable<EntityType> managedTypes = workspace.GetItems<EntityType>(DataSpace.OSpace);
var typeList = new List<Type>();
foreach (var managedType in managedTypes) {
var pocoType = managedType.FullName.GetCoreType();
typeList.Add(pocoType);
}
return typeList;
}

Related

Entity Framework Core - Setting Value Converter generically

I'm currently trialing Entity Framework Core 2.1 with a view to using it in the company I work for's business applications. I've got most of the way in implementing Value Converters in my test project but my existing knowledge base has let me down at the last hurdle!
What I'm trying to do
My understanding is that for enum values, the built in type converters can convert from the enum value to the string equivalent (EnumToStringConverter) or from the enum value to it's numerical representation (EnumToNumberConverter). However we use a custom string value to represent the enum in our database, so I have written a custom EnumToDbStringEquivalentConvertor to do this conversion and the database string value is specified as an attribute on each of the enum values in my model.
The code is as follows:
Model
public class User
{
[Key] public int ID { get; set; }
public EmployeeType EmployeeType { get; set; }
}
public enum EmployeeType
{
[EnumDbStringValue("D")]
Director,
[EnumDbStringValue("W")]
Weekly,
[EnumDbStringValue("S")]
Salaried
}
DataContext
public class MyDataContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (property.ClrType.IsEnum)
{
property.SetValueConverter(new EnumToDbStringEquivalentConvertor<EmployeeType>());
}
}
}
}
}
Value Converter
public class EnumToDbStringEquivalentConvertor<T> : ValueConverter<T, string>
{
public EnumToDbStringEquivalentConvertor(ConverterMappingHints mappingHints = null) : base(convertToProviderExpression, convertFromProviderExpression, mappingHints)
{ }
private static Expression<Func<T, string>> convertToProviderExpression = x => ToDbString(x);
private static Expression<Func<string, T>> convertFromProviderExpression = x => ToEnum<T>(x);
public static string ToDbString<TEnum>(TEnum tEnum)
{
var enumType = tEnum.GetType();
var enumTypeMemberInfo = enumType.GetMember(tEnum.ToString());
EnumDbStringValueAttribute enumDbStringValueAttribute = (EnumDbStringValueAttribute)enumTypeMemberInfo[0]
.GetCustomAttributes(typeof(EnumDbStringValueAttribute), false)
.FirstOrDefault();
return enumDbStringValueAttribute.StringValue;
}
public static TEnum ToEnum<TEnum>(string stringValue)
{
// Code not included for brevity
}
}
This code (I'm glad to say) seems to be working without any issues.
My problem
The documentation around value converters seems to suggest the way we assign them in the OnModelCreating method is to physically assign each individual type converter to each individual property in the model. I don't want to have to do this - I want my model to be the driver. I'll implement this later but, for now, in the current version of the code I'm looping through the entity types in my model, checking the 'IsEnum' property value and then assigning the value converter at that point.
My problem is that the SetValueConverter extension method that I'm using requires me to pass it a new instance of EnumToDbStringEquivalentConvertor, which in my example is hard coded to be EnumToDbStringEquivalentConvertor which works. However I don't want that to be hardcoded - I want to pass the entity type's ClrType.
I have used reflection to create generic types and generic methods before but I can't seem to find the right code to get this working.
This:
public class MyDataContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (property.ClrType.IsEnum)
{
var converterType = typeof(EnumToDbStringEquivalentConvertor<>);
var genericConverterType = converterType.MakeGenericType(property.ClrType);
MethodInfo setValueConverterMethodInfo = typeof(MutablePropertyExtensions).GetMethod("SetValueConverter");
setValueConverterMethodInfo.Invoke(property,
new object[] { property, Activator.CreateInstance(genericConverterType) });
}
}
}
}
}
gives me an error of "System.MissingMethodException: 'No parameterless constructor defined for this object.'" on the GetModel method in Microsoft.EntityFrameworkCore.Infrastructure
So my question is can anyone advise me of how I can pass my value converter generically to EF Core's 'SetValueConveter' method?
Thank you in advance for your assistance.
You are almost there. The problem is this code
Activator.CreateInstance(genericConverterType)
which tries to find and invoke parameterless constructor of your converter class. But your class constructor does have a parameter, although optional. Optional parameters are just compiler sugar; when using reflection you should pass them explicitly.
So you need to use the CreateInstance overload accepting params object[] args and pass null for mappingHints.
Also, there is no need to call SetValueConverter via reflection - it's part of the public API.
The working code could be like this:
if (property.ClrType.IsEnum)
{
var converterType = typeof(EnumToDbStringEquivalentConvertor<>)
.MakeGenericType(property.ClrType);
var converter = (ValueConverter)Activator.CreateInstance(converterType, (object)null);
property.SetValueConverter(converter);
}

Subclass Reflection type error

I'm currently having some issues with a method I made. I use reflection to run through my class and get all it's properties. I use this to cast my models to DTO and vice-versa.
The problem I am encountering is that, whenever my class has another class as an attribute, I get an error.
Object of type 'UserTypeProxy' cannot be converted to type 'MyNamespace.DTO.UserTypeDto'.
This is my code:
public static T Cast<T>(object myobj)
{
Type _objectType = myobj.GetType();
Type target = typeof(T);
var x = Activator.CreateInstance(target, false);
var z = from source in _objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var d = from source in target.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
var propy = myobj.GetType().GetProperty(memberInfo.Name);
value = propy.GetValue(myobj, null);
propertyInfo.SetValue(x, value, null); //<-- this is the line that gives the error
}
return (T)x;
}
As a previous commenter states, this is not the kind of code you should be writing/maintaining yourself. Frameworks like AutoMapper were built specifically to solve the problem you are attacking - converting model objects to DTOs. The right long-term choice would be to start leveraging such a framework instead of reinventing the wheel.
In the meanwhile the following code is a short-term solution for your issue. Keep in mind that while this may solve the specific case you mention in your question, object mapping has many corner cases and eventually you will run into another. I would recommend only using this as a temporary fix until you migrate to using AutoMapper or a similar framework.
Based on your description and your code, here is an example which models your failure:
static void Main(string[] args)
{
var user = new UserModel
{
Name = "User McUserson",
Age = 30,
Buddy = new UserModel
{
Name = "Buddy McFriendly",
Age = 28
}
};
// This fails saying that UserModel cannot be converted to UserDto
var userDto = Cast<UserDto>(user);
}
class UserModel
{
public String Name { get; set; }
public int Age { get; set; }
public UserModel Buddy { get; set; }
}
class UserDto
{
public String Name { get; set; }
public int Age { get; set; }
public UserDto Buddy { get; set; }
}
The problem is that the Buddy property, unlike all the others, has a different type in the model and DTO classes. A UserModel is simply not assignable to a UserDto. The only exception to this is if the value is null.
For properties which are class types, instead of setting the target equal to the source you need to map the source type to the target type: UserModel -> UserDto. This can be done with a recursive call.
Before I show you the code which solves this issue, let's talk about naming for a minute. Calling your function Cast() is very misleading. The operation we are really doing here is taking some source object and mapping its property values onto some target object of a specific type (with possible recursive mappings for properties which are class types).
Given this terminology, here is some updated code which solves this specific issue:
public static T MapProperties<T>(object source)
{
return (T)MapProperties(source, typeof(T));
}
public static object MapProperties(object source, Type targetType)
{
object target = Activator.CreateInstance(targetType, nonPublic: false);
Type sourceType = source.GetType();
var sourcePropertyLookup = sourceType.GetProperties().ToDictionary(p => p.Name);
var targetPropertyLookup = targetType.GetProperties().ToDictionary(p => p.Name);
var commonProperties = targetPropertyLookup.Keys.Intersect(sourcePropertyLookup.Keys);
foreach (var commonProp in commonProperties)
{
PropertyInfo sourceProp = sourcePropertyLookup[commonProp];
PropertyInfo targetProp = targetPropertyLookup[commonProp];
object sourcePropValue = sourceProp.GetValue(source);
if(sourcePropValue == null || targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
{
targetProp.SetValue(target, sourceProp.GetValue(source));
}
else
{
object mappedValue = MapProperties(sourceProp.GetValue(source), targetProp.PropertyType);
targetProp.SetValue(target, mappedValue);
}
}
return target;
}
You can use this in the same way you've used your previous code:
static void Main(string[] args)
{
var user = new UserModel
{
Name = "User McUserson",
Age = 30,
Buddy = new UserModel
{
Name = "Buddy McFriendly",
Age = 28
}
};
// This works!
var userDto = MapProperties<UserDto>(user);
}
Aside from some optimizations the key differences from your code is in the if-else block. There we check if we can assign the source value to the target directly, in which case we do what your code was doing so far. Otherwise it assumes we need to recursively map the value over. This new section is what solves the issue of converting a source property of a model class type to a target property of a DTO class type.

Pass Property to the Method in C#

I need to pass selection of properties of some types(one type each time), assume this is my type:
public class Product {
[PrimaryKey]
public long Id { get; set; }
[DisplayName("Name")]
public string Title { get; set; }
[Foreignkey(Schema = "Products", Table = "MajorCategory", Column = "Id")]
[DisplayName("MCat")]
public string MajorCategory { get; set; }
[Foreignkey(Schema = "Products", Table = "Category", Column = "Id")]
[DisplayName("Cat")]
public string Category { get; set; }
public long CategoryId { get; set; }
[BoolAsRadio()]
public bool IsScanAllowed { get; set; }
}
So I need a way to pass the list of properties of this type to other Type(Target Type), and use property name, and attributes, and I don't need values, something like the following Pseudo-code:
List<Property> propertyList = new List<Property>();
propertyList.Add(Product.Id);
PropertyList.Add(Product.Title);
TargetType target = new TargetType();
target.Properties = propertyList;
public class TargetType {
public List<Property> Properties { get; set;}
GetAttributes() {
foreach(Property item in Properties){
Console.WriteLine(item.Name)
//Get Attributes
}
}
}
Is there any way to pass just like Product.Id and use name and attributes of that? I don't sure but maybe PropertyInfo can help, I think just can pass List of Object but in that case I can't use attributes and names, what is your suggestion to handle this? or something like this? if I am wrong at all so how can I implement it?
Funny, I was just answering a similar question, or at least I think it is.
It looks like you're trying to concatenate the properties of two types into one? You need an ExpandoObject:
http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject%28v=vs.100%29.aspx
For an implementation of a nested merge, see this:
C# deep/nested/recursive merge of dynamic/expando objects
Basically, you want a keyed list of properties, to start from. The following code will do that for any .NET object:
var props = object.GetType().GetProperties().ToDictionary<PropertyInfo, string>(prop => prop.Name);
And after that it depends on what precisely it is you want to achieve - a true copy of the object, merge with another, or just maintaining the list.
You can make use of reflection in .NET here:
List<PropertyInfo> propertyList = new List<PropertyInfo>();
Type productType = typeof (Product);
propertyList.Add(productType.GetProperty("Id"));
propertyList.Add(productType.GetProperty("Title"));
TargetType target = new TargetType();
target.Properties = propertyList;
public class TargetType {
public List<PropertyInfo> Properties { get; set;}
List<object> GetAttributes()
{
List<object> attributes = new List<object>();
foreach(PropertyInfo item in Properties)
{
Console.WriteLine(item.Name);
attributes.AddRange(item.GetCustomAttributes(true));
}
return attributes;
}
}
You can use a list of PropertyInfo, List<PropertyInfo> as the type of your TargetType .Properties. To get the properties you can try it like this using Reflection.
targetType.Properties = product.GetType().GetProperties().ToList();
You can build list of properties using expression trees, e.g. you can make something like this:
var propertiesListBuilder = new PropertiesListBuilder<Product>();
propertiesListBuilder
.AddProperty(_ => _.Id)
.AddProperty(_ => _.Title);
var target = new TargetType();
target.Properties = propertiesListBuilder.Properties;
The only concern here is performance, i.e. it might be not good idea to recreate such property lists over and over again, most probably they should be cached. At the same time you'll get intellisense, compiler checks and refactoring support for your property lists.
Below is a sample implementation of this stuff.
static class PropertyInfoProvider<T>
{
public static PropertyInfo GetPropertyInfo<TProperty>(Expression<Func<T, TProperty>> expression)
{
var memberExpression = (MemberExpression)expression.Body;
return (PropertyInfo)memberExpression.Member;
}
}
class PropertiesListBuilder<T>
{
public IEnumerable<PropertyInfo> Properties
{
get
{
return this.properties;
}
}
public PropertiesListBuilder<T> AddProperty<TProperty>(
Expression<Func<T, TProperty>> expression)
{
var info = PropertyInfoProvider<T>.GetPropertyInfo(expression);
this.properties.Add(info);
return this;
}
private List<PropertyInfo> properties = new List<PropertyInfo>();
}
typeof(Product).GetProperties() would give you all (public) properties as PropertyInfo[].
See also MSDN.

Get collection property of a specific type

I have a class MyDatabaseContext that has a series of DbSet collection properties:
public DbSet<EntityA> EntitiesA { get; set; }
public DbSet<EntityB> EntitiesB { get; set; }
public DbSet<EntityC> EntitiesC { get; set; }
I need to get the name of the collection given the type of the entity.
For example, I have "EntityB" and want to get as a result "EntitiesB".
I really wanted to avoid switch-case statements, since MyDatabaseContext is generated automatically (T4 templates).
if you just want the name of the property here you go. I would just refine the answer given by hunter. You can use the same method with string as return type.
public string GetEntitiName<T>() where T : class
{
PropertyInfo propInfo = typeof(MyDatabaseContext).GetProperties().Where(p => p.PropertyType == typeof(DbSet<T>)).FirstOrDefault();
string propertyName = propInfo.Name; //The string has the property name ..
return propertyName;
}
I tried a sample similar to your situation. Try replacing List with DbSet.
class Program
{
public static void GetEntities<T>() where T : class
{
var info = typeof(TestClass1).GetProperties().Where(p => p.PropertyType == typeof(List<T>));
Console.WriteLine(info.FirstOrDefault().Name);
}
static void Main(string[] args)
{
GetEntities<int>();
Console.ReadLine();
}
}
public class TestClass1
{
public List<int> IntTest { get; set; }
public List<double> DoubleTest { get; set; }
public List<string> IStringTest { get; set; }
}
This sample works.
I know this is old page, But my answer maybe useful for other guys referring here. (like me)
I think you want to accessing EntitiesB to run a query on it, like EntitiesB.Where(a=>a.bla=="blabla"). If I'm right or another visitor of this page needs something like this, just easily use the following code:
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>()
Description:
_dbContext is Context class inherting from DbContext.
EntitiesB is DbSet<EntityB> defined in Context class.
Example:
Ilist result = ((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>().Where(b=>b.bla=="blabla").ToList();
Your generated file is a partial class, you could create a new file and declare a class with same name using the keyword partial, then make a method which will return the desired Collection...
I haven't actually done this myself, but it sounds like what you want to do is to use reflection to locate the property of type "DbSet" that has the appropriate generic type parameter. The following pseudo-C# should get you started:
foreach ( FieldInfo field in this.GetType() )
{
if ( field.FieldType.IsGenericType )
{
foreach ( Type param in field.FieldType.GetGenericArguments() )
{
if ( param.Name == soughtType )
{
return field.Name;
}
}
}
}

Accessing C# property name or attributes

I would like to automatically generate SQL statements from a class instance. The method should look like Update(object[] Properties, object PrimaryKeyProperty). The method is part of an instance (class, base method - generic for any child). Array of properties is an array of class properties, that will be used in update statement. Property names are equal to table field names.
The problem is that I can't get property names.
Is there any option to get a property name inside class instance?
sample:
public class MyClass {
public int iMyProperty { get; set; }
public string cMyProperty2 { get; set; }
{
main() {
MyClass _main = new MyClass();
_main.iMyProperty.*PropertyName* // should return string "iMyProperty"
{
I am aware of PropertyInfo, but I don't know hot to get the ID of a property from GetProperties() array.
Any suggestion?
Just wrote an implementation of this for a presentation on lambdas for our usergroup last Tuesday.
You can do
MembersOf<Animal>.GetName(x => x.Status)
Or
var a = new Animal()
a.MemberName(x => x.Status)
the code:
public static class MembersOf<T> {
public static string GetName<R>(Expression<Func<T,R>> expr) {
var node = expr.Body as MemberExpression;
if (object.ReferenceEquals(null, node))
throw new InvalidOperationException("Expression must be of member access");
return node.Member.Name;
}
}
Link to the presentation and code samples.
Also in SVN (more likely to be updated): http://gim-projects.googlecode.com/svn/presentations/CantDanceTheLambda
I found a perfect solution in This Post
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
And then for the usage :
var propertyName = GetPropertyName(
() => myObject.AProperty); // returns "AProperty"
Works like a charm
You can do something like this:
Type t = someInstance.getType();
foreach (MemberInfo mi in t.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
Console.WriteLine(mi.Name);
}
}
to get all the property names for instance's type.
You can get the name (I assume that's what you meant by ID) of a property using PropertyInfo.Name. Just loop through the PropertyInfo[] returned from typeof(className).GetProperties()
foreach (PropertyInfo info in typeof(MyClass).GetProperties())
{
string name = info.Name;
// use name here
}
Since you already have an explicit handle to the specific property you want, you know the name - can you just type it?
Not 100% sure if this will get you what you're looking for, this will fetch all properties with [Column] attribute inside your class:
In the datacontext I have:
public ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity>( )
{
return this.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(typeof(TEntity)).DataMembers;
}
Fetching the table column-names that are properties inside the class:
MyDataContext db = GetDataContext();
var allColumnPropertyNames = db.ColumnNames<Animal>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
Let's say (from the first sample, method update of a class MyClass):
public class MyClass {
public int iMyStatusProperty { get; set; }
public int iMyKey { get; set; }
public int UpdateStatusProperty(int iValue){
this.iMyStatusProperty = iValue;
return _Update( new[iMyStatusProperty ], iMyKey); // this should generate SQL: "UPDATE MyClass set iMyStatusProperty = {iMyStatusProperty} where iMyKey = {iMyKey}"
}
{iMyStatusProperty} and {iMyKey} are property values of a class instance.
So, the problem is how to get property name (reflection) from a property without using names of properties as strings (to avoid field name typos).

Categories