I have a list of static methods that all essentially do the same thing, just on different tables/ entity classes:
public static List<FormFieldHistoryViewModel> GetTextLabelHistory(Guid entryId, int? formFieldId)
{
List<FormFieldHistoryViewModel> history = new List<FormFieldHistoryViewModel>();
List<dbo_FormFieldsFileValues_CT> fields = new List<dbo_FormFieldsFileValues_CT>();
using (var ctx = new Entities())
{
fields = ctx.dbo_FormFieldsFileValues_CT.Where(f => f.FormFieldsID == formFieldId && f.EntryID == entryId).ToList();
}
foreach (var row in fields)
{
var ffhvm = new FormFieldHistoryViewModel();
ffhvm.DateEdited = row.DateEdited;
ffhvm.EditedBy = row.EditedBy;
ffhvm.Value = row.FileName;
history.Add(ffhvm);
}
return history;
}
Instead of having one method for each table/ entity object, I'd like to just pass a reference to that class as an argument to be used at every place where you see edmxobject. How can I achieve this?
public static List<FormFieldHistoryViewModel> GetTextLabelHistory(Guid entryId, int? formFieldId, type edmxobject)
{
List<FormFieldHistoryViewModel> history = new List<FormFieldHistoryViewModel>();
List<edmxobject> fields = new List<edmxobject>();
using (var ctx = new Entities())
{
fields = ctx.edmxobject.Where(f => f.FormFieldsID == formFieldId && f.EntryID == entryId).ToList();
}
foreach (var row in fields)
{
var ffhvm = new FormFieldHistoryViewModel();
ffhvm.DateEdited = row.DateEdited;
ffhvm.EditedBy = row.EditedBy;
ffhvm.Value = row.FileName;
history.Add(ffhvm);
}
return history;
}
}
You can pass in the type parameter using generics via T.
GetTextLabelHistory<T>(Guid entryId, int? formFieldId) where T : {constraint}
In order to operate on your data context using this type, you can use the Set method of DbContext
myDbContext.Set<T>().SomeOperation()
DbContext.Set()
Generic Constraints
To get something like this to work nice and tidy you will need a base interface for your Edited* fields. Something like:
public interface IEditable
{
DateTime DateEdited { get; }
string EditedBy { get; } // Assuming it stores a name.
string FileName { get; }
}
Then each supported class inherit this interface. The method would change to:
public List<FormFieldHistoryViewModel> GetTextLabelHistory<T>(Guid entryId, int? formFieldId) where T : class, IEditable
Then your query to extract common fields from entities would be:
using (var ctx = new Entities())
{
fields = ctx.Set<T>()
.Where(f => f.FormFieldsID == formFieldId && f.EntryID == entryId)
.Select(f => new FormFieldHistoryViewModel
{
DateEdited = f.DateEdited,
EditedBy = f.EditedBy,
Filename = f.Filename
}).ToList();
return fields;
}
The issue here though is likely going to be filtering, as this assumes that every table is going to have EntryId and FormFieldId which I assume may not be the case given your example was for a table FormFieldsFileValues.
If you want to do something similar for completely dissimilar entities, the IEditable can help expose the common fields, but you'll probably be out of luck actually querying the appropriate records within the method itself. The same goes for if you have different desired output view models. Again, your example had it returning a view model that had a FileName which aligns with FormFieldFileValues, but this won't suit cases where you want to return different view models.
Generic methods are suitable when the implementation across all supported classes is identical, not just similar. The output of a generic method must either be a value, a single common type, or a generic class associated with the applicable type. We can handle filtering via different sources (Key names, criteria etc.) by designing the method to accept an IQueryable<T> rather than querying inside the method, but this still requires a singular output class applicable across all implementations. For example:
public List<HistoryViewModel> GetHistory<T>(IQueryable<T> query) where T : class, IEditable
{
string sourceType = typeof(T).Name;
return query.Select(f => new HistoryViewModel
{
SourceType = sourceType,
SourceId = f.Id,
DateEdited = f.DateEdited,
EditedBy = f.EditedBy,
}).ToList();
}
Where the IEditable exposes an Id column rather than class specific details, and it is up to the caller to build a query for what to get history view models for.
If you do want different specific history view models to pair up with different unrelated source entities then a Generic implementation likely isn't up to the task.
Related
I need to return one row of List from my function Selectus.
So I pass to the function Selectus object that reflects database table fields and I need to return one row which match the parameter looking_for:
public static List<T> Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.ToList();//here one record should be returned
}
I do not know how to select one row from the list assuming that T parameter is vary. In SelectusT> method I want to pass as T different objects which reflect fields in database table rather than creatinig separate methods for each select. e.g. call Selectus, where object passed is public class ProductCodes { public int ID { get; set; } public string SapIndex { get; set; } public string SapName { get; set; } }. Then I want to call another Selectus<ProductTypes> for another table etc... So I want to write generic/overall method and use it universally for all types of my objects which reflects the fields of few database tables. The SapIndex property is always in the same place of all objects...
Using prop[1] is incredibly fragile. Who says that the property you're currently interested in is always going to be in second place? What if someone adds another property tomorrow? What if not every T that you use have the same property in the second place on its list of properties? It is quite unclear what your actual goal is here and why you've taken the reflection route.
You would be better off using inheritance or interface implementation here. I'm going to use an interface in this answer, but either would work.
For the sake of clarity, let's assume there is a Code field in all your possible lists, and this is the property you're trying to match with.
Define a reusable interface:
public interface ICodeEntity
{
string Code { get; }
}
Apply your interface to all of the classes that you intend to use for your Selectus method.
public class Person : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
public class Document : ICodeEntity
{
public string Code { get; set; }
// And other properties
}
Add a generic type constraint that limits the use of T only to types that implement your interface.
public static List<T> Selectus<T>(string code)
where T : ICodeEntity
You can now write your code in a way that it relies on the type in question having a Code property, and the compiler will help enforce it.
var db = OrmLiteBaza().Open();
var list = db.Select<T>().ToList();
db.Dispose();
return list.Where(item => item.Code == code).ToList();
Usage examples:
List<Person> peopleWithCodeABC = Selectus<Person>("ABC");
List<Person> documentsWithCodeXYZ = Selectus<Document>("XYZ");
// This will fail if Animal does not implement ICodeEntity
var compilerError = Selectus<Animal>("ABC");
I might not understand fully what you want, but instead of string looking_for you could pass in a Func<,> delegate which acts as a selector.
Something like:
public static List<TField> Selectus<T, TField>(Func<T, TField> selector)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Select(selector); // 'using System.Linq;'
return list_selected_record.ToList();
}
Then I believe it could be called like this:
var list_one = Selectus((ProductCodes x) => x.SapIndex);
var list_two = Selectus((ProductTypes x) => x.SapIndex);
var list_three = Selectus((ProductCodes x) => x.SapName);
With this syntax I leave out the <ProductCodes, string> generic arguments to the method since they can be inferred.
Hmm, maybe you want it in the opposite dimension. You could do:
public static List<T> Selectus<T>(Func<T, bool> predicate)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
var list_selected_record = select_all_list.Where(predicate); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus((ProductCodes x) => x.SapIndex == "ABC");
var list_two = Selectus((ProductTypes x) => x.SapIndex == "ABC");
var list_three = Selectus((ProductCodes x) => x.SapName == "DaName");
or:
var list_one = Selectus<ProductCodes>(x => x.SapIndex == "ABC");
var list_two = Selectus<ProductTypes>(x => x.SapIndex == "ABC");
var list_three = Selectus<ProductCodes>(x => x.SapName == "DaName");
But if it is going to always be the "same" property, like always x.SapIndex (but for different types of x), then Flater's answer looks good.
Otherwise, if you insist, your reflection approach should be possible. Use propety's name, not its index! Let me try:
public static List<T> Selectus<T>(string looking_for)
{
var db = OrmLiteBaza().Open();
var select_all_list = db.Select<T>();
db.Dispose();
const string prop_name = "SapIndex";
var prop = typeof(T).GetProperty(prop_name); // can blow up for bad T
var list_selected_record = select_all_list
.Where(x => (string)(prop.GetValue(x)) == looking_for); // 'using System.Linq;'
return list_selected_record.ToList();
}
with:
var list_one = Selectus<ProductCodes>("ABC");
var list_two = Selectus<ProductTypes>("ABC");
you can change code to return just one element
public static T Selectus<T>(string looking_for)
{
//all select data
var db = OrmLiteBaza().Open();//opening database
var select_all_list = db.Select<T>();//getting all data for <T> object works fine
db.Dispose();
//try to select one row - here I have trouble:
var prop = typeof(T).GetProperties();//properties of passed <T> object
var list_selected_record = from records in select_all_list where prop[1].Name == looking_for select records;//tryin to select one record from <T> object as in looking_for variable
return list_selected_record.FirstOrDefault();//here one record should be returned
}
How do I find the biggest Id of a DbSet.Set<T>()?
Note: not DbSet<TEntity>.
I don't know the type at runtime.
Context: I have 20 tables/entities, which I'm using a generic method to do processing.
The process involves looking up the biggest Id of that table/entity and comparing it with the record at hand.
If the record's id is bigger than the database's, than it would be inserted into the database.
So far I've tried using reflection:
DbSet<T> table = DbContext.Set<T>();
var lastRecord = table.LastOrDefault(); // throws not supported error
var idProperty = lastRecord.GetType().GetProperties()
.FirstOrDefault(p => p.Name.Equals("Id");
int maxId = (int)idProperty.GetValue(lastRecord);
I've also tried using an interface cast:
interface ICommonEntity
{ // this interface allows the generic method
string StringId { get;} // to know how to handle entity Id's of
int? IntId { get; } // different types (string vs int).
}
var whatever = table.OrderByDescending(e => (e as ICommonEntity).IntId).FirstOrDefault();
int maxId = (whatever as ICommonEntity).IntId ?? 0;
But the above yields the following error:
The 'TypeAs' expression with an input of type xx is not supported. and a check of type yy. Only entity types and complex types are supported in LINQ to Entities queries
Additional data: All my entities have the column/property Id of type int.
Web searches that I've done mainly point to solutions that the type is known e.g. TEntity, db.Users.xxx() etc..
Update
In response to Ian's answer, I can't use Id directly. Why?
One of my entity has a field named Id, but is of type string.
class EntityStringId : ICommonEntity
{
public string Id { get; set; }
public string StringId => Id;
public int? IntId => null;
}
class EntityIntId : ICommonEntity
{
public int Id { get; set; }
public string StringId => null;
public int? IntId => Id;
}
And if I try to use IntId for ordering,
private void SomeMethod<T>(string file)
//where T : class // original
//where T : ICommonEntity // cannot. DbContext.Set<T>(); requires class
where T : class, ICommonEntity // throws exception
{
var table_T = DbContext.Set<T>();
var maxId = table_T.Max(e => e.IntId); // throws exception ↓
}
The specified type member 'IntId' is not supported in LINQ to Entities.
Only initializers, entity members, and entity navigation properties are supported.
For a better picture, my method's logic:
private void ProcessCsvToDb<T>(
DbSet<T> table,
T csvRecord) where T : class
{
var iRecord = csvRecord as ICommonEntity;
T dbRecord = null;
if (!string.IsNullOrEmpty(iRecord.StringId))
{
dbRecord = table.Find(iRecord.StringId);
}
else if (iRecord.IntId != null)
{
dbRecord = table.Find(iRecord.IntId);
}
}
In order to do this without a base class/interface, you will need to manually compose the expression:
public static IOrderedQueryable<int> OrderById(Type entityType)
{
var dbSet = context.Set(entityType);
var item = Expression.Parameter(entityType, "item");
var property = Expression.Property(item, "Id");
var lambda = Expression.Lambda<Func<T, int>>(property, item);
// the above generates:
// item => item.Id
return dbSet.OrderByDescending(lambda);
}
You can build expression to sort by Id, but DynamicQueryable class does it for you:
DbSet<T> table = assignFromSomeWhere();
var maxId = table.OrderBy("Id desc").FirstOrDefault();
DynamicQueryable also gives you different extension methods (dynamic Where, Select). Obviously it is bigger satisfaction to build expressions on your own, but sometimes it is very complicated and this library helps a lot.
If you have an interface, as discussed in comments, is there any reason you can't do this to avoid the cast:
public static int? GetMaxId<T>(DBSet<T> dbSet)
where T : ICommonEntity
{
return dbSet.OrderByDescending(e => e.Id).FirstOrDefault();
}
I want to query data from sequence (IEnumerable and IQueryable) by nested Interface type, for example,
public interface IData
{
TypeInEnum? Value1 { get; set; }
string Value2 { get; set; }
}
public class DataModel : IData
{
public int? Value1 { get; set; }
public string Value2 { get; set; }
TypeInEnum? IData.Value1
{
get
{
return Value1.HasValue ? (TypeInEnum?)Value1.Value : null;
}
set
{
this.Commit = variable;
}
set
{
//ignore enum validation here
this.Value1 = value.HasValue ? (int?)value.Value : null;
}
}
}
public enum TypeInEnum
{
A = 1,
B,
C
}
query:
//source is IEnumerable<DataModel>
var query = source.Where(item => item.Value1 == 1); //item is DataModel
var query1 = source.Where1(item => item.Value1 == TypeInEnum.A); //item is IData
Assert.IsTrue(query.SequenceEqual(query1));
but this only works when Property in class and interface are same type. such as,
when use Where, the error is:
System.InvalidOperationException: Rewriting child expression from type 'System.Nullable<TypeInEnum>' to type 'System.Nullable<System.Int32>' is not allowed, because it would change the meaning of the operation. If this is intentional, override 'VisitUnary' and change it to allow this rewrite.
use Select, the error is:
System.ArgumentException: Expression of type 'System.Nullable<System.Int32>' cannot be used for return type 'System.Nullable<TypeInEnum>'
I don't know where to add a Convert.
all example code here
I have waste time on this for a month...
Edited
In my current project, which using EntityFramework, there are some basic columns in database of each table, but I found some basic column name are different, for example, CreatedDateTime and DateTimeCreated. There will be problems when put tables which contains basic column with different name into one Entity Data Model. Change these column name in database and project will be difficult and cause some new problems, there are a lot of svn branches, and some table are used in many modules. So I create an interface which contains all these basic columns, and change enum field from numeric type (in database) to enum type (in project), and let EF generated class implement this interface, if column name and type are not same, implement property in interface explicitly, therefore impact on the original project can be ignored.
This does solve the problem, but it's difficult to use the interface through EF, such as query data and modified value based on interface then save to database, create some common query extension based on interface. If this is possible, lots of codes can be reduced and the project will be easier to maintain.
Query data from database with same type in both entity model and interface is done, even if the field name is different.
You can use OfType to filter to only those that are assignable to a type.
Example:
var query = source.OfType<DataModel>().Where(item => item.Value1 == 1);
var query1 = source.OfType<IData>().Where(item => item.Value1 == TypeInEnum.A);
Could you try casting the item to IData in the Where predicate?
var query = source.Where(item => item.Value1 == 1); //item is DataModel
var query1 = source.Where(item => ((IData)item).Value1 == TypeInEnum.A); //item is IData
Assert.IsTrue(query.SequenceEqual(query1));
The above code works for me with casting.
A technical explanation and fix would be that that you can't compare a Nullable int and a Nullable enum without casting (this is not like comparing an int and an enum!).
At the end of this post you will find an example of how you can fix that.
However, the more interesting question is why do you have a
class DataModel with public int? Value1 member
that implements
Interface IData which declares a TypeInEnum? Value1 member?
I would argue that if DataModel implements IData it should also have Value1 of typeTypeInEnum? i.e.:
public class DataModel : IData
{
public TypeInEnum? Value1 { get; set; }
...
}
You can find an example for refactored code here
As for the technicahl solution:
Take a look at your code with a casting a fix:
https://dotnetfiddle.net/8MqXnr
var query = source.Where(item => item.Value1.HasValue).Where(item => (int)(item.Value1.Value) == 1); //item is DataModel
var query1 = source.Where(item => item.Value1.HasValue).Where(item => item.Value1.Value == (int)TypeInEnum.A); //item is IData
var eq = query.SequenceEqual(query1);
Console.WriteLine(String.Format("results: {0}",eq? "Equal": "Not equal"));
Consider the following scenario:
public class DestinationType1 { }
public class DestinationType2 { }
public class SourceType { }
public class SourceTypeA : SourceType { }
public class SourceTypeB : SourceType { }
I set up these mappings:
Mapper.CreateMap<SourceType, DestinationType2>();
Mapper.CreateMap<SourceTypeB, DestinationType2>();
Mapper.CreateMap<SourceType, DestinationType1>();
Mapper.CreateMap<SourceTypeA, DestinationType1>();
Then try to map the following:
var sourceTypes = new List<SourceType>{new SourceTypeA(), new SourceTypeB()};
var destinationType1s = Mapper.Map<List<DestinationType2>>(sourceTypes);
var destinationType2s = Mapper.Map<List<DestinationType1>>(sourceTypes);
What i want to achieve is for destinationType1s to only have one member, mapped from the SourceTypeA in the source list, and destinationType2s to only have one mapped from SourceTypeB. However what i get is two elements in both lists mapped from both the source types.
Is this achievable somehow out of the box or do i need to write my own value resolver or similar?
You could use the OfType LINQ extension method to filter the sourceTypes list.
var sourceTypes = new List<SourceType>{new SourceTypeA(), new SourceTypeB()};
var destinationType1s = Mapper.Map<List<DestinationType1>>(sourceTypes.OfType<SourceTypeA>());
var destinationType2s = Mapper.Map<List<DestinationType2>>(sourceTypes.OfType<SourceTypeB>());
OfType<type> will produce an IEnumerable<type> so you could also remove the 2 maps for the base SourceType if you're not going to use them.
If you want to filter for more than one type, then you could create your own extension method similar to OfType that takes a list of types or takes the DestinationType and looks up which types are mapped to it. Here's something that works using Mapper.FindTypeMapFor to filter only compatible types:
//Mapper.CreateMap<SourceType, DestinationType2>(); -- don't want this!
Mapper.CreateMap<SourceTypeB, DestinationType2>();
//Mapper.CreateMap<SourceType, DestinationType1>(); -- don't want this!
Mapper.CreateMap<SourceTypeA, DestinationType1>();
var sourceTypes = new List<SourceType> { new SourceTypeA(), new SourceTypeB() };
var destinationType1s = Mapper.Map<List<DestinationType1>>(sourceTypes.CompatibleMappedTypes<DestinationType1>());
var destinationType2s = Mapper.Map<List<DestinationType2>>(sourceTypes.CompatibleMappedTypes<DestinationType2>());
...
static class Extensions
{
public static IEnumerable CompatibleMappedTypes<TDestination>(this IEnumerable source)
{
foreach (var s in source)
{
if (Mapper.FindTypeMapFor(s.GetType(), typeof(TDestination)) != null) yield return s;
}
}
}
I'm currently struggling with finding a better way to populate my ViewModel objects with my Entitiy objects. I have the following Web Api controller method:
[HttpGet]
public IEnumerable<ClientSearchViewModel> FindClients(string query)
{
var clients = _clientService.SearchClient(query).ToList();
var returnClients = new List<ClientSearchViewModel>();
foreach (var client in clients)
{
returnClients.Add(new ClientSearchViewModel(client));
}
return returnClients;
}
And I'm doing this in my ClientSearchViewModel constructor:
public ClientSearchViewModel(Client client)
{
this.Id = client.Id;
this.FirstName = client.PersonName.FirstName;
this.LastName = client.PersonName.LastName;
}
Is there another way other than going through the list of returned objects and creating a new ViewModel list?
I strongly suggest use of a mapping plugin for this, such as:
AutoMapper
or
ValueInjector
Plugins like this will allow you to map between the objects being used internally or in your data layer, with your external objects (DTOs/ViewModels). They handle a number of things out of the box such as automatic mapping of any like named properties with the same type, but also allow for a lot of control in the specific mapping of properties or types, for those times when you need something more custom.
For a brief comparison of the two, there isn't much better than hearing the authors themselves respond: AutoMapper vs ValueInjecter
Personally, I find ValueInjector to be quicker to use, while having more control overall, but I also find it to be much less readable/inuitive than AutoMapper, which can require a bit more code to accomplish similar goals. As such, I'd pick the one that you find you and/or your team will prefer the syntax of and how easily you can grasp the concepts vs how much power you really need.
So I had the same miff... I can't say that I've benchmarked my solution, but it does seem to run reasonably fast...
3 bits:
public static T Transform<T>(this object convertFrom) where T : class, new()
{
return (T) (new ServiceExtension().Transform(convertFrom, typeof (T)));
}
private class ServiceExtension
{
public object Transform(object convertFrom, Type convertTo)
{
object _t = Activator.CreateInstance(convertTo);
if (convertFrom == null) return _t;
var convertType = convertFrom.GetType();
foreach (
var property in _t.GetType().GetProperties().Where(f => f.CanWrite && f.GetSetMethod(true).IsPublic)
)
{
if (property.GetCustomAttributes(typeof (TransformAttribute), true).Any())
{
var transform =
(property.GetCustomAttributes(typeof (TransformAttribute), true).FirstOrDefault() as
TransformAttribute);
var transformname = transform.RelatedField ?? property.Name;
if (convertType.GetProperty(transformname) == null)
throw new ArgumentException(
string.Format(
"We were unable to find property:\"{0}\" on {1}. Please check the RelativeField value on the {2} for \"{0}\"",
transformname, convertFrom.GetType().Name, convertTo.Name));
var theValue = convertType.GetProperty(transformname).GetValue(convertFrom);
if (isCollection(theValue))
{
foreach (var item in (theValue as ICollection))
{
var someVal = new object();
var newToType = property.PropertyType.GetGenericArguments().FirstOrDefault();
if (!String.IsNullOrEmpty(transform.FullyQualifiedName))
someVal =
Transform(
item.GetType().GetProperty(transform.FullyQualifiedName).GetValue(item),
newToType);
else
someVal = Transform(item, newToType);
if (property.GetValue(_t) == null)
throw new NullReferenceException(
string.Format(
"The following property:{0} is null on {1}. Likely this needs to be initialized inside of {1}'s empty constructor",
property.Name, _t.GetType().Name));
property.PropertyType.GetMethod("Add")
.Invoke(property.GetValue(_t), new[] {someVal});
//property.SetValue(_t, theValue.Transform(theValue.GetType()));
}
}
else
property.SetValue(_t, theValue);
}
//property.SetValue(_t, property.GetValue(convertFrom, null), null);
}
return _t;
}
public bool isCollection(object o)
{
return o is ICollection
|| typeof (ICollection<>).IsInstanceOfType(o);
}
}
public class TransformAttribute : Attribute
{
public string RelatedField { get; private set; }
public string FullyQualifiedName { get; set; }
public TransformAttribute()
{
}
public TransformAttribute(string relatedField)
{
RelatedField = relatedField;
}
}
such that the end result is: myObject.Transform()
But the decorations let you account for differences between your POCO and your ViewModel