Convert Entity with navigation property to DTO using IQueryable - c#

Suppose I have following entities and dtos
public class Country
{
public List<NameLocalized> NamesLocalized;
public CountryData Data;
}
public class NameLocalized
{
public string Locale;
public string Value;
}
public class CountryData
{
public int Population;
}
public class CountryDto
{
public String Name;
public CountryDataDto Data;
}
public class CountryDataDto
{
public int Population;
}
I need to convert Country to CountryDto (and ideally, I want to make a single query to the database). I have received few suggestions in my other questions on Stackoverflow, and can now accomplish the task, but only partially. I am stuck at how to convert navigation property (CountryData in this case). I was suggested to use LINQKit for this, but have no idea how to implement it. Here is my code which populates only Name property but not Data navigation property.
public static async Task<List<CountryDto>> ToDtosAsync(this IQueryable<Country> source, string locale)
{
if(source == null)
{
return null;
}
var result = await source
.Select(src => new CountryDto
{
Name = src.NamesLocalized.FirstOrDefault(n => n.Locale == locale).Name
})
.ToListAsync();
return result;
}

This answer gave me the hint for my solution. You need to use LINQKit and build Expression to convert the navigation property.
public static Expression<Func<CountryData, CountryDataDto>> ConverterExpression = cd => new CountryDataDto
{
Population = cd.Population
};
public static async Task<List<CountryDto>> ToDtosAsync(this IQueryable<Country> source, string locale)
{
if(source == null)
{
return null;
}
var result = await source
.AsExpandable
.Select(src => new CountryDto
{
Name = src.NamesLocalized.FirstOrDefault(n => n.Locale == locale).Name
Data = ConverterExpression.Invoke(src.Data)
})
.ToListAsync();
return result;
}

Related

How to make the placehoder in Generics generic

I am using Strategy Pattern, I have heaps of rules and I need to check all rows in Azure storage table against each Rule.
interface IRule where TEntity : TableEntity, new()
{
string TableName { get; } // It could be "ContractAccount", "Bill", "Transaction" etc.
string Rule { get; }
string SaveToTable { get; }
TableQuery<TEntity> TableQuery { get; }
ReportEntity Handle(TableEntity entity);
}
So instance of rules lives inside the Validator.
public Validator()
{
Rules = new List<IRule>();
Rules.Add(new AddressRule());
}
The Table Entity class(ContractAccount.cs Bill.cs etc.) will have the same name as the value IRule.TableName holds.
So this is where the ContractAccount comes from.
Then in the Validator, I have Validate() which looks like:
public async void Validate(CloudStorageAccount storageAccount)
{
var tableClient = storageAccount.CreateCloudTableClient();
//.....
var query = new TableQuery<ContractAccount>(); //<-- I want to replace ContractAccount with something generic
//...
var rows = await tableToBeValidated.ExecuteQuerySegmentedAsync(query, token);
}
//...
}
In my AddressRule.cs
public class AddressRule : IRule<ContractAccount>
{
public string TableName => "ContractAccount";
public string Rule => "Email cannot be empty";
public string SaveToTable => "XXXX";
public TableQuery<ContractAccount> TableQuery => new TableQuery<ContractAccount>();
public ReportEntity Handle(TableEntity entity)
{
var contract = entity as ContractAccount;
if(contract == null)
{
throw new Exception($"Expecting entity type {TableName}, but passed in invalid entity");
}
if (string.IsNullOrWhiteSpace(contract.Address))
{
var report = new ReportEntity(this.Rule, contract.UserId, contract.AccountNumber, contract.ContractNumber)
{
PartitionKey = contract.UserId,
RowKey = contract.AccountNumber
};
return report;
}
return null;
}
}
As you can see
var query = new TableQuery<ContractAccount>();
I need to replace the Hard-coded with something like:
var type = Type.GetType(tableName);
var query = new TableQuery<type>();
but the placeholder(ContractAccount) will change when app is running, it could be Bill, Policy, Transaction etc....
I cannot use the <T> thing.
How can I replace the ContractAccount with a generic thing?
Update 2
After applied Juston.Another.Programmer's suggection, I got this error.
Update 3
Now I updated code to below:
interface IRule<TEntity> where TEntity : TableEntity
{
string TableName { get; }
string Rule { get; }
string SaveToTable { get; }
ReportEntity Handle(TableEntity entity);
TableQuery<TEntity> GetTableQuery();
}
Which I specified what type of class the TEntity has to be, it removes the 1st error, but the 2nd error persists:
Error CS0310 'TEntity' must be a non-abstract type with a public
parameterless constructor in order to use it as parameter 'TElement'
in the generic type or method 'TableQuery'
Update 4
I found how to fix the another error:
interface IRule<TEntity>
where TEntity : TableEntity, new()
But then, I have problem to add my AddressRule into Rules in the Validator class.
public Validator()
{
Rules = new List<IRule<TableEntity>>();
var addressRule = new AddressRule();
Rules.Add(addressRule);
}
Something like this:
var genericType = typeof(TableQuery<>);
Type[] itemTypes = { Type.GetType("MyNamespace.Foo.Entities." + tableName) };
var concretType = genericType.MakeGenericType(itemTypes);
var query = Activator.CreateInstance(concretType);
You could use reflection like #Christoph suggested, but in this case there's an easier approach. Add a TEntity generic parameter to your IRule class instead of using the TableName string property and add a GetTableQuery method to the class.
interface IRule<TEntity>
{
string Rule { get; }
string SaveToTable { get; }
ReportEntity Handle(TableEntity entity);
TableQuery<TEntity> GetTableQuery();
}
Then, in your IRule<TEntity> implementations add the correct entity. Eg for AddressRule.
public class AddressRule : IRule<ContractAcccount>
{
public string TableName => "ContractAccount";
public string Rule => "Email cannot be empty";
public string SaveToTable => "XXXX";
public ReportEntity Handle(TableEntity entity)
{
var contract = entity as ContractAccount;
if(contract == null)
{
throw new Exception($"Expecting entity type {TableName}, but passed in invalid entity");
}
if (string.IsNullOrWhiteSpace(contract.Address))
{
var report = new ReportEntity(this.Rule, contract.UserId, contract.AccountNumber, contract.ContractNumber)
{
PartitionKey = contract.UserId,
RowKey = contract.AccountNumber
};
return report;
}
return null;
}
public TableQuery<ContractAccount> GetTableQuery()
{
return new TableQuery<ContractAccount>();
}
}
Now, in your Validate method, you can use the GetTableQuery method from the IRule.
public async void Validate(CloudStorageAccount storageAccount)
{
var tableClient = storageAccount.CreateCloudTableClient();
//.....
var query = rule.GetTableQuery();
//...
var rows = await tableToBeValidated.ExecuteQuerySegmentedAsync(query, token);
}
//...
}
The longer I think about it the more I get the feeling that what you need is a generic solution and not one with generics. I guess that the table client in line
var tableClient = storageAccount.CreateCloudTableClient();
does always return something like a DataTable or an object with an IEnumerable, independently of whether you ask for a ContractAccount or a Bill. If that's the case, it might be better to have a validator that loads all the rules of all entities from the database (or through factory patterns or hardcoded) and then applies the according ones to the given entity.
Like that, the set of rules can be defined using XML or some other sort of serialization (not part of this example) and only a few rule classes are needed (I call them EntityValidationRule).
The parent of all rules for all entities could look like this:
public abstract class EntityValidationRule {
//Private Fields
private Validator validator;
//Constructors
public EntityValidationRule(String tableName, IEnumerable<String> affectedFields) {
TableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
AffectedFields = affectedFields?.ToArray() ?? Array.Empty<String>();
}
//Public Properties
public String TableName { get; }
public String[] AffectedFields { get; }
public virtual String Description { get; protected set; }
//Public Methods
public Boolean IsValid(DataRow record, ref IErrorDetails errorDetails) {
if (record == null) throw new InvalidOperationException("Programming error in Validator.cs");
if (!Validator.IdentifyerComparer.Equals(record.Table.TableName, TableName)) throw new InvalidOperationException("Programming error in Validator.cs");
String myError = GetErrorMessageIfInvalid(record);
if (myError == null) return true;
errorDetails = CreateErrorDetails(record, myError);
return false;
}
//Protected Properties
public Validator Validator {
get {
return validator;
}
internal set {
if ((validator != null) && (!Object.ReferenceEquals(validator, value))) {
throw new InvalidOperationException("An entity validation rule can only be added to a single validator!");
}
validator = value;
}
}
//Protected Methods
protected virtual IErrorDetails CreateErrorDetails(DataRow record, String errorMessage) {
return new ErrorDetails(record, this, errorMessage);
}
protected abstract String GetErrorMessageIfInvalid(DataRow record);
}
and to stay with your example, the sample implementation for an empty text field check could look like this (having an intermediate class OneFieldRule):
public abstract class OneFieldRule : EntityValidationRule {
public OneFieldRule(String tableName, String fieldName) : base(tableName, new String[] { fieldName }) {
}
protected String FieldName => AffectedFields[0];
}
and like this:
public class TextFieldMustHaveValue : OneFieldRule {
public TextFieldMustHaveValue(String tableName, String fieldName) : base(tableName, fieldName) {
Description = $"Field {FieldName} cannot be empty!";
}
protected override String GetErrorMessageIfInvalid(DataRow record) {
if (String.IsNullOrWhiteSpace(record.Field<String>(FieldName))) {
return Description;
}
return null;
}
}
Then the central validator that works like a service to validate whatever entity needs to be validated I might implement like this:
public sealed class Validator {
//Private Fields
private Dictionary<String, List<EntityValidationRule>> ruleDict;
//Constructors
//The list of all rules we just have somehow...
public Validator(IEnumerable<EntityValidationRule> rules, StringComparer identifyerComparer) {
if (rules == null) throw new ArgumentNullException(nameof(rules));
if (identifyerComparer == null) identifyerComparer = StringComparer.OrdinalIgnoreCase;
IdentifyerComparer = identifyerComparer;
ruleDict = new Dictionary<String, List<EntityValidationRule>>(IdentifyerComparer);
foreach (EntityValidationRule myRule in rules) {
myRule.Validator = this;
List<EntityValidationRule> myRules = null;
if (ruleDict.TryGetValue(myRule.TableName, out myRules)) {
myRules.Add(myRule);
} else {
myRules = new List<EntityValidationRule> { myRule };
ruleDict.Add(myRule.TableName, myRules);
}
}
}
//Public Properties
public StringComparer IdentifyerComparer { get; }
//Public Methods
public Boolean IsValid(DataRow record, ref IErrorDetails[] errors) {
//Check whether the record is null
if (record == null) {
errors = new IErrorDetails[] { new ErrorDetails(record, null, "The given record is null!") };
return false;
}
//Loop through every check and invoke them
List<IErrorDetails> myErrors = null;
IErrorDetails myError = null;
foreach (EntityValidationRule myRule in GetRules(record.Table.TableName)) {
if (myRule.IsValid(record, ref myError)) {
if (myErrors == null) myErrors = new List<IErrorDetails>();
myErrors.Add(myError);
}
}
//Return true if there are no errors
if (myErrors == null) return true;
//Otherwise assign them as result and return false
errors = myErrors.ToArray();
return false;
}
//Private Methods
private IEnumerable<EntityValidationRule> GetRules(String tableName) {
if (ruleDict.TryGetValue(tableName, out List<EntityValidationRule> myRules)) return myRules;
return Array.Empty<EntityValidationRule>();
}
}
And the error details as an interface:
public interface IErrorDetails {
DataRow Entity { get; }
EntityValidationRule Rule { get; }
String ErrorMessage { get; }
}
...and an implementation of it:
public class ErrorDetails : IErrorDetails {
public ErrorDetails(DataRow entity, EntityValidationRule rule, String errorMessage) {
Entity = entity;
Rule = rule;
ErrorMessage = errorMessage;
}
public DataRow Entity { get; }
public EntityValidationRule Rule { get; }
public String ErrorMessage { get; }
}
I know this is a totally different approach as you started off, but I think the generics give you a hell of a lot of work with customized entities that have customized validators for each and every table in your database. And as soon as you add a table, code needs to be written, compiled and redistributed.

How to fill some properties in model after evaluation of IQueryable in OData?

I'm using OData with Entity Framework. Let's assume that I have following models and controller method:
public class Model1
{
public int Id { get; set; }
public int Field2 { get; set; }
public int FieldFromOtherService { get; set; }
public Model2 Model2 { get; set; } // Navigation Property
public int Model2Id { get; set; }
}
public class Model2
{
public int Id { get; set; }
public int Field { get; set; }
}
[HttpGet, EnableQuery]
public IQueryable<Model1> Get()
{
return modelRepository.List();
}
Model1 has property FieldFromOtherService that is not taken from DB - it is retrieved from other service. I need a way to fill this property after applying OData top, skip, expand and select clause.
Is there a way to accomplish that? I've tried to make a wrapper to IQueryable and call action after evaluation but it crash when query is more complicated.
Finally, I manage to accomplish my goals with #zaitsman suggestion. It was harder then I thought because OData adds wrappers that are not accessible (classes SelectAllAndExpand, SelectAll, SelectSomeAndInheritance, SelectSome). When expand is used, it is necessary to extract DTO from the wrapper. My code looks more or less like this:
[HttpGet]
public IHttpActionResult Get(ODataQueryOptions<Model1> options)
{
var result = modelRepository.List();
Action<ICollection<Model1>> postAction = collection => { Console.WriteLine("Post Action"); };
return ApplyOdataOptionsAndCallPostAction(result, options, postAction);
}
private IHttpActionResult ApplyOdataOptionsAndCallPostAction<T>(
IQueryable<T> baseQuery,
ODataQueryOptions<T> options,
Action<ICollection<T>> postAction)
where T : class
{
var queryable = options.ApplyTo(baseQuery);
var itemType = queryable.GetType().GetGenericArguments().First();
var evaluatedQuery = ToTypedList(queryable, itemType);
var dtos = ExtractAllDtoObjects<T>(evaluatedQuery).ToList();
postAction(dtos)
return Ok(evaluatedQuery, evaluatedQuery.GetType());
}
private static IList ToTypedList(IEnumerable self, Type innerType)
{
var methodInfo = typeof(Enumerable).GetMethod(nameof(Enumerable.ToList));
var genericMethod = methodInfo.MakeGenericMethod(innerType);
return genericMethod.Invoke(null, new object[]
{
self
}) as IList;
}
private IEnumerable<T> ExtractAllDtoObjects<T>(IEnumerable enumerable)
where T : class
{
foreach (var item in enumerable)
{
if (item is T typetItem)
{
yield return typetItem;
}
else
{
var result = TryExtractTFromWrapper<T>(item);
if (result != null)
{
yield return result;
}
}
}
}
private static T TryExtractTFromWrapper<T>(object item)
where T : class
{
if (item is ISelectExpandWrapper wrapper)
{
var property = item.GetType().GetProperty("Instance");
var instance = property.GetValue(item);
if (instance is T val)
{
return val;
}
}
return null;
}
private IHttpActionResult Ok(object content, Type type)
{
var resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
}

Retrieving Navigation Properties with NHibernate Criteria

I think I'm just searching for the wrong terms, since I can't seem to find an answer to what I'm fairly sure is a simple question.
I have two classes/mappings (simplified);
public class Applications
{
public int Id {get; set;}
public string Name {get; set;}
public ApplicationType {get; set;}
public IEnumerable<ApplicationProperty> ApplicationProperties {get; set;}
}
public class ApplicationProperties
{
public int Id {get; set;}
public int Application_Id {get; set}
public string Value {get; set;}
}
I'm trying to build up a Criteria class in a (possibly misguided) attempt to make our code more readable/reusable, as follows;
public static class ApplicationsQuery
{
public static ICriteria GetQuery(ISession session)
{
return session.CreateCriteria(typeof(Application));
}
public static ICriteria WithType(this ICriteria crit, ApplicationType type)
{
crit.Add(
Restrictions.Eq(
Projections.ProjectionList().Add(
Projections.Property<Application>(a => a.ApplicationType)), type));
return crit;
}
public static ICriteria WithProperties(this ICriteria crit)
{
// What goes here?!
}
}
So that I can do something like
ICriteria query = ApplicationsQuery.GetQuery(session).WithType(ApplicationType.GameServer).WithProperties();
I've tried various things such as;
DetachedCriteria properties =
DetachedCriteria.For<Application>()
.SetProjection(Projections.Property<Application>(a => a.ApplicationProperties));
return crit.Add(Subqueries.Select(properties));
// Or this
return crit.SetFetchMode("ApplicationProperties", FetchMode.Eager);
But I'm unable to get the ApplicationProperties to be populated.
My test setup looks like this;
session.Save(new Application("Test Application 1", ApplicationType.GameServer), 1);
session.Save(new Application("Test Application 2", ApplicationType.Manager), , 2);
session.Save(new ApplicationProperty(1, "Test Property"), 1);
EDIT
Adding in the mappings as I get the feeling that there might be an issue with them.
public class ApplicationMapping : ClassMap<Application>
{
public ApplicationMapping()
{
Table("Applications");
Id(o => o.Id, "Application_Id").GeneratedBy.Assigned();
Map(o => o.Name).Column("Logical_Name");
Map(o => o.ApplicationType).Column("Application_Type").CustomType<ApplicationType>();
HasMany(o => o.ApplicationProperties).KeyColumn("Application_Id").ReadOnly().Cascase.None();
}
}
public class ApplicationPropertyMapping : ClassMap<ApplicationProperty>
{
public ApplicationPropertyMapping()
{
Table("Application_Properties");
Id(o => o.Id).GeneratedBy.Identity().Column("Property_Id");
Map(o => o.ApplicationId).Column("Application_Id");
Map(o => o.Value).Column("Property_Value");
}
}
Update: i stripped down the code
public class ApplicationsQueryBuilder
{
private static readonly IProjection ApplicationTypeProperty = Projections.Property<Application>(a => a.ApplicationType);
private readonly DetachedCriteria _query = DetachedCriteria.For<Application>();
private bool _withProperties;
private bool _filteredByCollection;
public ApplicationsQueryBuilder WithType(ApplicationType type)
{
_query.Add(Restrictions.Eq(ApplicationTypeProperty, type));
return this;
}
public ApplicationsQueryBuilder WithTypes(params ApplicationType[] types)
{
var or = Restrictions.Disjunction();
foreach (var type in types)
{
or.Add(Restrictions.Eq(ApplicationTypeProperty, type));
}
_query.Add(or);
return this;
}
public ApplicationsQueryBuilder WithProperties()
{
_withProperties = true;
return this;
}
public ApplicationsQueryBuilder WithProperty(string name)
{
_query.CreateCriteria("ApplicationProperties")
.Add(Restrictions.Eq("Name", name));
_filteredByCollection = true;
return this;
}
...
public ICriteria Create(ISession session)
{
if (_withProperties && _filteredByCollection)
{
_query.SetProjection(Projections.Id());
return session.CreateCriteria<Application>()
.SetFetchMode("ApplicationProperties", FetchMode.Eager);
.Add(Subqueries.PropertyIn("Id", _query));
}
else if (_withProperties)
{
return _query.GetExecutableCriteria(_session)
.SetFetchMode("ApplicationProperties", FetchMode.Eager);
}
else
{
return _query.GetExecutableCriteria(session);
}
}
}

Dynamic LINQ query to get Field value from Database

is it possible?
Public String Get_Filed_By_Id(string table_Name,String Field_Name,string PK_val)
{
string strRes="";
using(mydbcontext db=new mydbcontext())
{
var x=db.table_Name.Where(p=>p.Id=PK_val).FirstOrDefault().Field_Name;
strRes=Convert.Tostring(x);
}
return strRes;
}
OR
var x=(from o in db.table_Name where o.Id=PK_val select o.Field_Name).FirstOrDefault();
Here, i'm passing Table_Name,Column_Name and the Condition value(PK_val) to Get the Column_Name from Table_Name within a Certain Condition(Id=Pk_val).
Is it possible??
Is it possible??
Yes, it is.
First, some helpers:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace YourNamespace
{
internal static class DbHelpers
{
public static object GetColumnById(this object dbContext, string tableName, string columnName, object id)
{
var table = (IQueryable)dbContext.GetType().GetProperty(tableName).GetValue(dbContext, null);
var row = Expression.Parameter(table.ElementType, "row");
var filter = Expression.Lambda(Expression.Equal(Expression.Property(row, "Id"), Expression.Constant(id)), row);
var column = Expression.Property(row, columnName);
var selector = Expression.Lambda(column, row);
var query = Call(Where.MakeGenericMethod(row.Type), table, filter);
query = Call(Select.MakeGenericMethod(row.Type, column.Type), query, selector);
var value = Call(FirstOrDefault.MakeGenericMethod(column.Type), query);
return value;
}
private static readonly MethodInfo Select = GetGenericMethodDefinition<
Func<IQueryable<object>, Expression<Func<object, object>>, IQueryable<object>>>((source, selector) =>
Queryable.Select(source, selector));
private static readonly MethodInfo Where = GetGenericMethodDefinition<
Func<IQueryable<object>, Expression<Func<object, bool>>, object>>((source, predicate) =>
Queryable.Where(source, predicate));
private static readonly MethodInfo FirstOrDefault = GetGenericMethodDefinition<
Func<IQueryable<object>, object>>(source =>
Queryable.FirstOrDefault(source));
private static MethodInfo GetGenericMethodDefinition<TDelegate>(Expression<TDelegate> e)
{
return ((MethodCallExpression)e.Body).Method.GetGenericMethodDefinition();
}
private static object Call(MethodInfo method, params object[] parameters)
{
return method.Invoke(null, parameters);
}
}
}
and now your function:
public string Get_Field_By_Id(string table_Name, string field_Name, string PK_val)
{
using (var db = new mydbcontext())
return Convert.ToString(db.GetColumnById(table_Name, field_Name, PK_val));
}
It is not really possible with EntityFramework actually(as far as I know). If you only needed the field by its name, then you could have used #Den's proposed solution. But you want to specify the table name too as a parameter. So I suggest you to use standard Sql Connector api, and build the query string with the parameters you provide.
Check this link for usage of standard sql connector api.
I had this question too ,I know this is not exactly what you want and you need write more code but it's much cleaner than those you want to write.
Using repository pattern
For every table you should have a model class and Repository class.
Consider this code(this code from one of my project)
This is my comment table(this can be anything with or without navigation property)
public sealed class Comment
{
public string CommentText { get; set; }
public DateTime PostDate { get; set; }
public int PostId { get; set; }
public int? PageId { get; set; }
public Page Page { get; set; }
public User User { get; set; }
public string UserId { get; set; }
public int? ParentId { get; set; }
public Comment[] ChildComments { get; set; }
}
RepositoryComment
public sealed class CommentRepository : BaseRepository<Comment>
{
public CommentRepository(BabySitterContext context)
: base(context)
{
}
}
and a base class that you send your query with table name(here model) and field(you can extend clas for more functionality)
public class BaseRepository<T> where T : class
{
protected BabySitterContext Context;
private readonly PluralizationService _pluralizer = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en"));
public BaseRepository(BabySitterContext context)
{
this.Context = context;
}
public bool Add(T t)
{
Context.Set<T>().Add(t);
Context.SaveChanges();
return true;
}
public bool Update(T t)
{
var entityName = GetEntityName<T>();
object originalItem;
var key = ((IObjectContextAdapter)Context).ObjectContext.CreateEntityKey(entityName, t);
if (((IObjectContextAdapter)Context).ObjectContext.TryGetObjectByKey(key, out originalItem))
{
((IObjectContextAdapter)Context).ObjectContext.ApplyCurrentValues(key.EntitySetName, t);
}
Context.SaveChanges();
return true;
}
public void Attach(T t)
{
if (t == null)
{
throw new ArgumentNullException("t");
}
Context.Set<T>().Attach(t);
Context.SaveChanges();
}
public void Remove(T t)
{
if (t == null)
{
throw new ArgumentNullException("t");
}
Context.Set<T>().Remove(t);
Context.SaveChanges();
}
public IEnumerable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
{
IQueryable<T> query = Context.Set<T>();
if (filter != null)
{
query = query.Where(filter.Expand());
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
else
{
return query.ToList();
}
}
private string GetEntityName<TEntity>() where TEntity : class
{
return string.Format("{0}.{1}", ((IObjectContextAdapter)Context).ObjectContext.DefaultContainerName, _pluralizer.Pluralize(typeof(TEntity).Name));
}
public virtual IEnumerable<T> GetByBusinessKey(T entity)
{
return null;
}
}
For any other table just make model class and reposiotry then inherite from base class
Using code
var context = new BabySitterContext();
var _commentRepository = new CommentRepository(context);
var comment = _commentRepository.Get(x => x.PostId == id).FirstOrDefault();
No, but in this way
Public String Get_Filed_By_Id(string table_Name,String Field_Name,string PK_val)
{
string strRes="";
using(mydbcontext db=new mydbcontext())
{
var x=db.table_Name.Where(p=>p.Id=PK_val).Select(b=>b.Field_Name).FirstOrDefault();
strRes=Convert.Tostring(x);
}
return strRes;
}

Linq sorting of entity by custom property via reflection

Got Customer class which has Country property which has string property Name.
Also Customer implements IComparable<Country> like so:
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
Now:
var custList = new List<Customer>{...};
custList.OrderBy(cust => cust.Country).ToList(); //Sorts as charm.
And if try sorting via reflection:
var itemProp = typeof(Customer).GetProperty("Country");
custList = c.Customers.ToList()
.OrderBy(cust => itemProp.GetValue(cust, null)).ToList(); // Fails
Throws exception 'At least one object must implement IComparable'
Please explain why does it fail and how correctly implement sorting of Customer by custom property via reflection. Thanks.
Since GetValue returns Object you need to implement the non generic version of IComparable.
void Main()
{
var custList = new List<Customer>()
{
new Customer(){ Country = new Country(){ Name = "Sweden" } },
new Customer(){ Country = new Country(){ Name = "Denmark" } },
};
var itemProp = typeof(Customer).GetProperty("Country");
custList = custList.OrderBy(cust => itemProp.GetValue(cust, null)).ToList();
custList.Dump();
}
public class Country : IComparable<Country>, IComparable
{
public string Name {get;set;}
public int CompareTo(Country other)
{
return string.Compare(this.Name, other.Name);
}
public int CompareTo(object other)
{
var o = other as Country;
if(o == null)
return 0; //Or how you want to handle it
return CompareTo(o);
}
}
public class Customer
{
public Country Country{get;set;}
}
Assuming that the underlying type is correct (i.e. Country), you should be able to do it as long as Country implements IComparable:
Here's a sample console app that works correctly (note that there is no error handling):
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Number: IComparable<Number>, IComparable
{
public Number(int value)
{
Value = value;
}
public readonly int Value;
public int CompareTo(Number other)
{
return Value.CompareTo(other.Value);
}
public int CompareTo(object obj)
{
return CompareTo((Number) obj);
}
}
class Test
{
public Number Number;
public object Obj
{
get { return Number; }
}
public override string ToString()
{
return Number.Value.ToString();
}
}
internal static class Program
{
static void Main()
{
var itemProp = typeof(Test).GetProperty("Obj");
Console.WriteLine(string.Join("\n",
data().OrderBy(x => itemProp.GetValue(x, null))));
}
static IEnumerable<Test> data()
{
for (int i = 0; i < 10; ++i)
yield return new Test {Number = new Number(10-i)};
}
}
}

Categories

Resources