Reflection + Linq + DbSet - linq

I use EF code-first 4.1. in my application. Now I want to get entities through WCF services using generic types.
I'm trying to reflect generic type and invoke the method ToList of DbSet Object.
Here is my code:
public string GetAllEntries(string objectType)
{
try
{
var mdc =
Globals.DbConnection.Create(#"some_db_connection", true);
// Getting assembly for types
var asob = Assembly.GetAssembly(typeof(CrmObject));
// getting requested object type from assembly
var genericType = asob.GetType(objectType, true, true);
if (genericType.BaseType == typeof(CrmObject))
{
// Getting Set<T> method
var method = mdc.GetType().GetMember("Set").Cast<MethodInfo>().Where(x => x.IsGenericMethodDefinition).FirstOrDefault();
// Making Set<SomeRealCrmObject>() method
var genericMethod = method.MakeGenericMethod(genericType);
// invoking Setmethod into invokeSet
var invokeSet = genericMethod.Invoke(mdc, null);
// invoking ToList method from Set<> invokeSet
var invokeToList = invokeSet.GetType().GetMember("ToList").Cast<MethodInfo>().FirstOrDefault();
//this return not referenced object as result
return invokeToList.ToString();
}
return null;
}
catch (Exception ex)
{
return ex.Message + Environment.NewLine + ex.StackTrace;
}
}
In fact then I write the code like return mdc.Set<SomeRealCrmObject>().ToList() - works fine for me! But then I use the generic types I cannot find the method ToList in object DbSet<SomeRealCrmObject>().

Eranga is correct, but this is an easier usage:
dynamic invokeSet = genericMethod.Invoke(mdc, null);
var list = Enumerable.ToList(invokeSet);
C# 4's dynamic takes care of the cumbersome generic reflection for you.

ToLIst() is not a member of DbSet/ObjectSet but is an extension method.
You can try this instead
var method = typeof(Enumerable).GetMethod("ToList");
var generic = method.MakeGenericMethod(genericType);
generic.Invoke(invokeSet, null);

I had a similar situation where I needed a way to dynamically load values for dropdown lists used for search criteria on a page allowing users to run adhoc queries against a given table. I wanted to do this dynamically so there would be no code change on the front or backend when new fields were added. So using a dynamic type and some basic reflection, it solved my problem.
What I needed to do was invoke a DbSet property on a DbContext based on the generic type for the DbSet. So for instance the type might be called County and the DbSet property is called Counties. The load method below would load objects of type T (the County) and return an array of T objects (list of County objects) by invoking the DbSet property called Counties. EntityList is just a decorator object that takes a list of lookup items and adds additional properties needed for the grid on the front-end.
public T[] Load<T>() where T : class
{
var dbProperty = typeof(Data.BmpDB).GetProperties().FirstOrDefault(
x => x.GetMethod.ReturnType.GenericTypeArguments[0].FullName == typeof(T).FullName);
if (dbProperty == null)
return null;
dynamic data = dbProperty.GetMethod.Invoke(BmpDb, null);
var list = Enumerable.ToList(data) as List<T>;
var entityList = new Data.EntityList<T>(list);
return entityList.Results;
}

Related

NHibernate returning results from Web API

I'm using Nhibernate to fetch a collection which has lazy loaded properties but am having trouble returning it as the Serializer tries to serialize the lazy property after the Nhibernate Session is closed. So is there a way to tell NHibernate to give me a true list in which if there were unloaded lazy collections that it would just leave them empty?
For example
IEnumerable<Store> stores = StoreService.GetList(1, 2);
Store has a one-to-many mapping with StockItems which is set to lazy load which then causes the serialization error. I tried
List<Store> stores_r = stores.ToList();
but I get the same thing. Is there something that will traverses through the list and fetches one-to-one relations and ignores one-to-many lazy loading and return a finished list?
Thanks
EDIT:Solution I've tried but still not working
public class NHibernateContractResolver: DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType) || typeof(NHibernate.Proxy.ILazyInitializer).IsAssignableFrom(objectType))
{
var oType = objectType.GetInterfaces().FirstOrDefault(i => i.FullName.StartsWith("Navace.Models"));
return oType != null ? base.CreateContract(oType) : base.CreateContract(objectType.BaseType);
}
return base.CreateContract(objectType);
}
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType))
{
return base.GetSerializableMembers(objectType.BaseType);
}
else
{
return base.GetSerializableMembers(objectType);
}
}
}
Try to manually serialize so I can use what's happening
IEnumerable<Store> stores = StoreService.GetList(1, 2);
Store> storess = stores.ToList();
JsonSerializer sr = new JsonSerializer
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new NHibernateContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
};
StringWriter stringWriter = new StringWriter();
JsonWriter jsonWriter = new Newtonsoft.Json.JsonTextWriter(stringWriter);
sr.Serialize(jsonWriter, storess);
string res = stringWriter.ToString();
The error I get is
Outer exception : Error getting value from 'datedcost' on 'PartProxy'.
Inner exception: No row with the given identifier exists[Navace.Models.Part#0]
My recommendation is to return view models instead of domain models. It's confusing to return an empty collection property when it may have data. By converting the domain model to a view model (using LINQ Select or AutoMapper), the serializer will only touch (and attempt to lazy load) the properties in the view model.

How to get a standalone / unmanaged RealmObject using Realm Xamarin

Is there a way that when I read an object from Realm that it can become a standalone or unmanaged object? In EF, this is called no tracking. The usage for this would be when I want to implement more business logic on my data objects before they are updated on the persistent data storage. I may want to give the RealmObject to a ViewModel, but when the changes come back from the ViewModel, I want to compare the disconnected object to the object in the datastore to determine what was changed, so If there was a way that I could disconnect the object from Realm when I give it to the ViewModel, then I can better manage what properties have changed, using my biz logic to do what I need, then save the changes back to realm.
I understand Realm does a lot of magic and many people will not want to add a layer like this but in my app, I cant really have the UI directly updating the datastore, unless there is a event that is raised that I can subscribe too and then attach my business logic this way.
I only saw one event and it does not appear to perform this action.
Thanks for your assistance.
First, get json NUGET :
PM> Install-Package Newtonsoft.Json
And, try this "hack" :
Deserialize the modified IsManaged property does the tricks.
public d DetachObject<d>(d Model) where d : RealmObject
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<d>(
Newtonsoft.Json.JsonConvert.SerializeObject(Model)
.Replace(",\"IsManaged\":true", ",\"IsManaged\":false")
);
}
.
If you facing slow-down on JsonConvert:
According to source code
, the 'IsManaged' property only has get accessor and return true when private field _realm which is available
So, we has to set the instance of field _realm to null does the tricks
public d DetachObject<d>(d Model) where d : RealmObject
{
typeof(RealmObject).GetField("_realm",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.SetValue(Model, null);
return Model.IsManaged ? null : Model;
}
.
You will get empty RealmObject body after Realm are now implemented same strategy as LazyLoad
Record down live RealmObject and (deactivate) realm instance in object by Reflection. And set back recorded values to RealmObject. With handled all the ILists inside too.
public d DetachObject<d>(d Model) where d : RealmObject
{
return (d)DetachObjectInternal(Model);
}
private object DetachObjectInternal(object Model)
{
//Record down properties and fields on RealmObject
var Properties = Model.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
.Where(x => x.Name != "ObjectSchema" && x.Name != "Realm" && x.Name != "IsValid" && x.Name != "IsManaged" && x.Name != "IsDefault")
.Select(x =>(x.PropertyType.Name == "IList`1")? ("-" + x.Name, x.GetValue(Model)) : (x.Name, x.GetValue(Model))).ToList();
var Fields = Model.GetType().GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
.Select(x => (x.Name, x.GetValue(Model))).ToList();
//Unbind realm instance from object
typeof(RealmObject).GetField("_realm", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).SetValue(Model, null);
//Set back the properties and fields into RealmObject
foreach (var field in Fields)
{
Model.GetType().GetField(field.Item1, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public).SetValue(Model, field.Item2);
}
foreach (var property in Properties.OrderByDescending(x=>x.Item1[0]).ToList())
{
if (property.Item1[0] == '-')
{
int count = (int)property.Item2.GetType().GetMethod("get_Count").Invoke(property.Item2, null);
if (count > 0)
{
if (property.Item2.GetType().GenericTypeArguments[0].BaseType.Name == "RealmObject")
{
for (int i = 0; i < count; i++)
{
var seter = property.Item2.GetType().GetMethod("set_Item");
var geter = property.Item2.GetType().GetMethod("get_Item");
property.Item2.GetType().GetField("_realm", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public).SetValue(property.Item2, null);
DetachObjectInternal(geter.Invoke(property.Item2, new object[] { i }));
}
}
}
}
else
{
Model.GetType().GetProperty(property.Item1, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public).SetValue(Model, property.Item2);
}
}
return Model;
}
.
For List of the RealmObject , Using Select():
DBs.All<MyRealmObject>().ToList().Select(t => DBs.DetachObject(t)).ToList();
.
(Java)You dont need this if youre in java:
Maybe some day, this feature will come to .NET Realm
Realm.copyFromRealm();
#xamarin #C# #Realm #RealmObject #detach #managed #IsManaged #copyFromRealm
Until its added to Realm for Xamarin, I added a property to my Model that creates a copy of the object. This seems to work for my use. The TwoWay Binding error messages are now also not an issue. For a more complicated application, I don't want to put business or data logic in the ViewModel. This allows all the Magic of xamarin forms to work and me to implement logic when its finally time to save the changes back to realm.
[Ignored]
public Contact ToStandalone()
{
return new Contact()
{
companyName = this.companyName,
dateAdded = this.dateAdded,
fullName = this.fullName,
gender = this.gender,
website = this.website
};
}
However, If there are any relationships this method does not work for the relationships. Copying the List is not really an option either as the relationship cant exist if the object is not attached to Realm, I read this some where, can't find it to ref now. So I guess we will be waiting for the additions to the framework.
Not currently in the Xamarin interface but we could add it. The Java interface already has copyFromRealm which performs a deep copy. That also has a paired merging copyToRealmOrUpdate.
See Realm github issue for further discussion.
However, as a design issue, is this really meeting your need in an optimal way?
I have used converters in WPF apps to insert logic into the binding - these are available in Xamarin Forms.
Another way in Xamarin forms is to use Behaviours, as introduced in the blog article and covered in the API.
These approaches are more about adding logic between the UI and ViewModel, which you could consider as part of the ViewModel, but before updates are propagated to bound values.
After wasting too much time in 3rd party libraries like AutoMapper, I've created my own extension function which works pretty well. This function simply uses Reflection with the recession. (Currently, Only for List type. You can extend the functionality for Dictionary and other types of the collection very easily or you can completely modify the functionality based on your own requirements.).
I didn't do too much time and complexity analysis. I've tested only for my test case which contains many nested RealmObject, built from a 3500+ line of JSON object, took only 15 milliseconds to clone the object.
Here the complete extension available via Github Gist. If you want to extend the functionality of this extension please update this Github Gist, So, other developers can take advantage of it.
Here the complete extension -
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using Realms;
namespace ProjectName.Core.Extensions
{
public static class RealmExtension
{
public static T Clone<T>(this T source) where T: new()
{
//If source is null return null
if (source == null)
return default(T);
var target = new T();
var targetType = typeof(T);
//List of skip namespaces
var skipNamespaces = new List<string>
{
typeof(Realm).Namespace
};
//Get the Namespace name of Generic Collection
var collectionNamespace = typeof(List<string>).Namespace;
//flags to get properties
var flags = BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance;
//Get target properties list which follows the flags
var targetProperties = targetType.GetProperties(flags);
//if traget properties is null then return default target
if (targetProperties == null)
return target;
//enumerate properties
foreach (var property in targetProperties)
{
//skip property if it's belongs to namespace available in skipNamespaces list
if (skipNamespaces.Contains(property.DeclaringType.Namespace))
continue;
//Get property information and check if we can write value in it
var propertyInfo = targetType.GetProperty(property.Name, flags);
if (propertyInfo == null || !property.CanWrite)
continue;
//Get value from the source
var sourceValue = property.GetValue(source);
//If property derived from the RealmObject then Clone that too
if (property.PropertyType.IsSubclassOf(typeof(RealmObject)) && (sourceValue is RealmObject))
{
var propertyType = property.PropertyType;
var convertedSourceValue = Convert.ChangeType(sourceValue, propertyType);
sourceValue = typeof(RealmExtension).GetMethod("Clone", BindingFlags.Static | BindingFlags.Public)
.MakeGenericMethod(propertyType).Invoke(convertedSourceValue, new[] { convertedSourceValue });
}
//Check if property belongs to the collection namespace and original value is not null
if (property.PropertyType.Namespace == collectionNamespace && sourceValue != null)
{
//get the type of the property (currently only supported List)
var listType = property.PropertyType;
//Create new instance of listType
var newList = (IList)Activator.CreateInstance(listType);
//Convert source value into the list type
var convertedSourceValue = Convert.ChangeType(sourceValue, listType) as IEnumerable;
//Enumerate source list and recursively call Clone method on each object
foreach (var item in convertedSourceValue)
{
var value = typeof(RealmExtension).GetMethod("Clone", BindingFlags.Static | BindingFlags.Public)
.MakeGenericMethod(item.GetType()).Invoke(item, new[] { item });
newList.Add(value);
}
//update source value
sourceValue = newList;
}
//set updated original value into the target
propertyInfo.SetValue(target, sourceValue);
}
return target;
}
}
}

OData Get method returns Complex Type

I'm just getting started with OData using Asp.Net Web API.
I declare in WebApiConfig.cs
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Staff>("staffs");
return builder.GetEdmModel();
In StaffsController.cs
public class StaffsController : ODataController
{
UnitOfWork unitOfWork;
public StaffsController()
{
this.unitOfWork = new UnitOfWork();
}
[EnableQuery]
public IHttpActionResult Get()
{
var q = this.unitOfWork.StaffRepository.Data.Select(p => new { p.Name, p.Id, p.Ext });
// Or Grouping here to return a Complex Type
return Ok(q);
}
}
It return 406 error, but if I change
var q = this.unitOfWork.StaffRepository.Data.Select(p => new { p.Name, p.Id, p.Ext });
// Or Grouping here to return a Complex Type
to
var q = this.unitOfWork.StaffRepository.Data;
It works.
I've search on Google for a while but still found nothing. Could you help me out if we can do that or we have other ways to reach that?
The type that you pass into the generic parameter for builder.EntitySet - in your case Staff - needs to match the object that you return from the Get method. Because you used Staff, the variable q needs to be of type IQueryable<Staff>.
The Data property is of this type so it works, but the anonymous type for the version that isn't working obviously isn't so it doesn't work.
Following on from your comment
I want get Staff information list with some statistic data
In order to add different properties to the response, you need to create a new object type that you pass into the builder.EntitySet generic parameter and then create it in Select statement in the controller.
However, what you are doing in the example, seems to be just returning properties that are already on the Staff object. You could use an OData $select statement to do this. For example:
http://yoururl/staffs?$select=Name,Id,Ext

Is it Possible to use reflection on LINQ to Entities to query a dynamic table?

There are many tables in the database that are used as "lookup" tables. All the tables have the same structure, other than the ID column name.
I have found that I can use reflection to open a table and enumerate through the records. The method takes a string (tableName).
Uri serviceUri = new Uri("http://localhost/MyDataService/WcfDataService.svc");
var context = new MyEntities(serviceUri);
var eTable = typeof(MyEntities).GetProperty(tableName).GetValue(context, null) as IEnumerable<object>
foreach (object o in eTable)
...
This works fine, but I want to add a WHERE clause to the query. For example, where InactiveDate == null.
Can I do this? I have been unable to figure this one out.
How about this?
var eTable = (typeof(MyEntities).GetProperty(tableName).GetValue(context, null) as IEnumerable<object>).Where(obj => obj.GetType().GetProperty("InactiveDate").GetValue(obj) == null);
foreach (object o in eTable)
I would suggest to use generics and maybe an interface over reflection.
public function Xyz<TEntity>(Func<MyEntities, IDbSet<TEntity>> dbSetGetter, Expression<Func<TEntity, Boolean>> filter)
{
var serviceUri = new Uri("http://localhost/MyDataService/WcfDataService.svc");
using (var context = new MyEntities(serviceUri))
{
foreach (var entity in dbSetGetter(context).Where(filter))
{
DoSomethingWith(entity);
}
}
}
Usage would be like this
Xyz(context => context.Foo, foo => foo.Bar == 42);
assuming you have an entity Foo with an integer property Bar. The obvious difference to your code is that you have to know the entity type a compile time and I am not sure if you know it then.

IEqualityComparer exception

I am using Entity Framework 4.0 and trying to use the "Contains" function of one the object sets in my context object. to do so i coded a Comparer class:
public class RatingInfoComparer : IEqualityComparer<RatingInfo>
{
public bool Equals(RatingInfo x, RatingInfo y)
{
var a = new {x.PlugInID,x.RatingInfoUserIP};
var b = new {y.PlugInID,y.RatingInfoUserIP};
if(a.PlugInID == b.PlugInID && a.RatingInfoUserIP.Equals(b.RatingInfoUserIP))
return true;
else
return false;
}
public int GetHashCode(RatingInfo obj)
{
var a = new { obj.PlugInID, obj.RatingInfoUserIP };
if (Object.ReferenceEquals(obj, null))
return 0;
return a.GetHashCode();
}
}
when i try to use the comparer with this code:
public void SaveRatingInfo2(int plugInId, string userInfo)
{
RatingInfo ri = new RatingInfo()
{
PlugInID = plugInId,
RatingInfoUser = userInfo,
RatingInfoUserIP = "192.168.1.100"
};
//This is where i get the execption
if (!context.RatingInfoes.Contains<RatingInfo>(ri, new RatingInfoComparer()))
{
//my Entity Framework context object
context.RatingInfoes.AddObject(ri);
context.SaveChanges();
}
}
i get an execption:
"LINQ to Entities does not recognize the method 'Boolean Contains[RatingInfo](System.Linq.IQueryable1[OlafCMSLibrary.Models.RatingInfo], OlafCMSLibrary.Models.RatingInfo,
System.Collections.Generic.IEqualityComparer1[OlafCMSLibrary.Models.RatingInfo])' method, and his method cannot be translated into a store expression."
Since i am not proficient with linQ and Entity Framework i might be making a mistake with my use of the "var" either in the "GetHashCode" function or in general.
If my mistake is clear to you do tell me :) it does not stop my project! but it is essential for me to understand why a simple comparer doesnt work.
Thanks
Aaron
LINQ to Entities works by converting an expression tree into queries against an object model through the IQueryable interface. This means than you can only put things into the expression tree which LINQ to Entities understands.
It doesn't understand the Contains method you are using, so it throws the exception you see. Here is a list of methods which it understands.
Under the Set Methods section header, it lists Contains using an item as supported, but it lists Contains with an IEqualityComparer as not supported. This is presumably because it would have to be able to work out how to convert your IEqualityComparer into a query against the object model, which would be difficult. You might be able to do what you want using multiple Where clauses, see which ones are supported further up the document.

Resources