I am building a system of filters to manipulate a simple collection that looks like:
public class ProcessedData
{
public byte ProcessId { get; set; }
public List<EventsData> EventsData { get; set; } = new List<EventsData>();
}
And EventsData object has its id property inside something like:
public class EventData
{
public byte EventDataId { get; set; }
}
Now the idea is simple, I have a collection of selected ProcessId and EventId in form of the two list like:
public List<byte> SelectedProcessFilter { get; set; }
public List<byte> SelectedEventFilter { get; set; }
So I have prepared aggregate where conditions function what looks like:
public IEnumerable<ProcessedData> DataFilter(IEnumerable<ProcessedData> preprocessedData, Expression<Func<ProcessedData, bool>>[] filters)
{
if (filters != null)
{
preprocessedEvents = filters.Aggregate(preprocessedData, (current, filter) => current.Where(filter.Compile()));
}
return preprocessedEvents.ToList();
}
And now i can declare where conditions something like:
private Expression<Func<ProcessedData, bool>>[] BuildPredicate()
{
var predicates = new List<Expression<Func<ProcessedData, bool>>>();
if (SelectedProcessFilter != null && SelectedProcessFilter .Any())
{
predicates.Add(data => SelectedProcessFilter.Contains(data.ProcessId));
}
if (SelectedEventFilter != null && SelectedEventFilter.Any())
{
predicates.Add(data => SelectedEventFilter.Any(value => data.EventsData.Any(target => target.EventDataId == (byte)value.Value)));
}
return predicates.ToArray();
}
Usage is simple just:
IEnumerable<ProcessedData> filteredPreprocessedData = service.DataFilter(preprocessedEvents, BuildPredicate());
And basically works, but there is one thins when i filter on EventsDataId, it checks if such value is in that property only, Is it possible to fit somehow possibility to rebuild such property so it will contain only selected EventId ?
For now as what i did, I fallowed NetMage advise and just made a copy of object with new properties values.
Related
I have more than 100 tables in data base in which 60+ table's contain column called ShortCode nvarchar(12) which represent globally unique code of that record.
Now is there any way to find that the ShortCode value eg. AST_SHIP_FIRE present in any of the table in database.
Note:ShortCode is user define.
currently I am try below code,it works but I have to code for all table.
if (entities.Table1.Any(x => x.ShortCode.Trim().ToLower() == a.ShortCode.Trim().ToLower())
{return false;}
else if(entities.Table2.Any(x => x.ShortCode.Trim().ToLower() == a.ShortCode.Trim().ToLower()))
{return false;}
else if( entities.Talble3.Any(x => x.ShortCode.Trim().ToLower() == a.ShortCode.Trim().ToLower()))
{return false;}
.
.
.
else
{
//insert code
}
I think there may be more efficient way.
Ok, maybe not very straightforward but lets do it!
First of all define an interface for ShortCode property and implement it by any entity that has it:
public interface ITableWithShortCode
{
public string ShortCode { get; set; }
}
public class Table1 : ITableWithShortCode
{
public long Id { get; set; }
public string ShortCode { get; set; }
}
public class Table2 : ITableWithShortCode
{
public long Id { get; set; }
public string ShortCode { get; set; }
}
Now using power of Reflection you can write a method like this:
public bool IsExistShortCode(string shortCode)
{
using (var context = new AppDbContext())
{
/*
find all tables that are defined in your DbContext and are implemented ITableWithShortCode like:
public DbSet<Table1> Table1 { get; set; }
public DbSet<Table2> Table2 { get; set; }
...
*/
var properties = typeof(AppDbContext).GetProperties()
.Where(p => p.PropertyType.IsGenericType
&& typeof(ITableWithShortCode).IsAssignableFrom(p.PropertyType.GenericTypeArguments[0]));
foreach (var property in properties)
{
var contextProp = (IQueryable<ITableWithShortCode>)property.GetValue(context);
bool isExist = contextProp.Any(p => p.ShortCode == shortCode);
if (isExist)
return true;
}
return false;
}
}
Note: You can do some optimization on this code, I prefered to keep it in its simplest state to show the idea. But in production, for example you can easily cache DbContext properties on startup and use it afterward
I have a problem when I update a line with a foreign key. The principle idea is to update a row in the database with a generic method but I have an exception when I save modification in the data base so I try to make the state of entity os modified but not the worker.
else if (ModeButtonVMCaracteristiquesType == ModeButtonVMCaracteristiqueType.EDITIONCaracteristiqueTypeItem)
{
int idCaracteristiqueSelected = Convert.ToInt32(CaracteristiqueSelected.idCharacteristicItem);
var LineModified = (from x in ImItemsModel.imtypeitems select x.imcharacteristicsitems).ToList();
LineModified.ForEach(p => ImItemsModel.Entry(p).State = System.Data.Entity.EntityState.Modified);
var UpdateCaracteristicTypeItem = StaticGenericUpdate.UpadateRowInModel<imcharacteristicsitem>(ImItemsModel, "idCharacteristicItem", idCaracteristiqueSelected, "fk_idTypeItemIMCaracteristicsItems", fk_value, propertiesForModel, propertiesForView, this);
ImItemsModel.SaveChanges();
So I have two models:
public partial class imcharacteristicsitem
{
public imcharacteristicsitem()
{
this.imvaluesofitemscaracteristics = new HashSet<imvaluesofitemscaracteristic>();
}
public int idCharacteristicItem { get; set; }
public string characteristicItem { get; set; }
public string unitCaracteristicItem { get; set; }
public int fk_idTypeItemIMCaracteristicsItems { get; set; }
public byte[] typeValueCaracteristicItem { get; set; }
public virtual ICollection<imvaluesofitemscaracteristic> imvaluesofitemscaracteristics { get; set; }
public virtual imtypeitems imtypeitem { get; set; }
}
and:
public partial class imtypeitems
{
public imtypeitems()
{
this.imcharacteristicsitems = new HashSet<imcharacteristicsitem>();
this.imitems = new HashSet<imitem>();
}
public int idTypeItem { get; set; }
public string DesignationTypeItem { get; set; }
public byte[] SymbolTypeItem { get; set; }
public Nullable<int> MaxNumberConnectionsTypeItem { get; set; }
public virtual ICollection<imcharacteristicsitem> imcharacteristicsitems { get; set; }
public virtual ICollection<imitem> imitems { get; set; }
}
and the generic method is:
public static T UpadateRowInModel<T>(DbContext Model, string NameiD, int IdSelected, string NameFk_key ,int fk_key, IList<PropertyInfo> propertiesModel, IList<PropertyInfo> propertiesView, VMCaracteristicType vm) where T : class
{
T item = Model.Set<T>().Find(IdSelected);
foreach (var property in propertiesModel)
{
if (propertiesView.Count != 0)
{
property.SetValue(item, propertiesView.FirstOrDefault(elem => elem.Name.Equals(property.Name)) == null ? null :
propertiesView.FirstOrDefault(elem => elem.Name.Equals(property.Name)).GetValue(vm));
}
if (property.Name == NameiD)
{
property.SetValue(item, IdSelected);
}
if (property.Name == NameFk_key)
{
property.SetValue(item, null);
Model.Entry(item).Property(NameFk_key).IsModified = false;
}
}
return item;
}
EDIT :
so i realise that the probleme is the entity framework can't save because my table imtypeitems have a collection of caractéristique
public virtual ICollection<imcharacteristicsitem> imcharacteristicsitems { get; set; }
so i must delet the row that i wish update it from this table and after that i will save so i can't delet the row of collection i try like this :
var RowBeforupdate = ImItemsModel.imcharacteristicsitems.Include("imtypeitem").Single(row => row.idCharacteristicItem == idCaracteristiqueSelected);
var UpdateCaracteristicTypeItem = StaticGenericUpdate.UpadateRowInModel<imcharacteristicsitem>(ImItemsModel, "idCharacteristicItem", idCaracteristiqueSelected, "fk_idTypeItemIMCaracteristicsItems", fk_value, propertiesForModel, propertiesForView, this);
ImItemsModel.Entry(RowBeforupdate).CurrentValues.SetValues(UpdateCaracteristicTypeItem);
just a ps : i am not a expert in entity framework :(
The most likely cause for the FK null exception is that the FK entity property is processed/set in the foreach loop after the FK_ID property. So even if you change the FK_ID IsModified value to false, you're most likely later setting the imtypeitem property to null.
Also you are changing all model properties based on view properties; so if there's no particular view property you'll be setting the model property to null. This is seldom the thing you want to be doing in this scenario. Usually you want to change just the corresponding view properties and leave the other model properties unchanged.
So the foreach part of your generic method should look like this:
if (propertiesView.FirstOrDefault(elem => elem.Name.Equals(property.Name)) != null)
{
property.SetValue(item, propertiesView.First(elem => elem.Name.Equals(property.Name)).GetValue(vm));
}
If you have certain properties that are part of view properties but you don't want them to be updated you can just skip them (using your Nameid and NameFk_key):
if(property.Name == NameiD || property.Name == NameFk_key)
continue;
So the final form of the generic method might look something like this:
public static T UpadateRowInModel<T>(DbContext Model, string NameiD, int IdSelected, string NameFk_key ,int fk_key, IList<PropertyInfo> propertiesModel, IList<PropertyInfo> propertiesView, VMCaracteristicType vm) where T : class
{
T item = Model.Set<T>().Find(IdSelected);
foreach (var property in propertiesModel)
{
if(property.Name == NameiD || property.Name == NameFk_key)
continue;
var viewP = propertiesView.FirstOrDefault(elem => elem.Name.Equals(property.Name));
if (viewP != null)
{
property.SetValue(item, viewP.GetValue(vm));
}
}
return item;
}
There's also a variation possible where you do a foreach on the propertiesView; also the parameters for the method can be adjusted.
On a sidenote: I'm not sure if
var LineModified = (from x in ImItemsModel.imtypeitems select x.imcharacteristicsitems).ToList();
LineModified.ForEach(p => ImItemsModel.Entry(p).State = System.Data.Entity.EntityState.Modified);
does something useful, or if it does anything. The p in lambda is an ICollection of enity types and not a separate DB context entity. Not to mention that you want to set Modified state for ALL entities of type imcharacteristicsitem. Maybe you can explain the reason for this.
I am converting an Expression<T, bool> to an Expression<Y, bool> where T and Y are different entities not related in any way other than through an Automapper mapping. Essentially, I have a Model object that my code uses:
public class Store
{
public string StoreId { get; set; }
public string Name { get; set; }
public List<Phone> Phones { get; set; }
public Address Address { get; set; }
public Account Account { get; set; }
public Status Status { get; set; }
}
that I am mapping to an entity object to store in my mongo database:
public class Store : MongoEntity
{
public string AccountId { get; set; }
public string Name { get; set; }
public List<string> UserIds { get; set; }
public List<Phone> PhoneNumbers { get; set; }
public Address Address { get; set; }
}
public abstract class MongoEntity : IMongoEntity
{
[BsonId]
public ObjectId Id { get; set; }
public Status Status { get; set; }
}
I used the answer in this question to work out how to convert between expressions (Question), and that got me 90% there. I was able to modify the code to grab the AutoMapper mappings between my Model store and my entity store and grab the destination property from from the source property:
private Expression<Func<TNewTarget, bool>> TransformPredicateLambda<TOldTarget, TNewTarget>(
Expression<Func<TOldTarget, bool>> predicate)
{
var lambda = (LambdaExpression)predicate;
if (lambda == null)
{
throw new NotSupportedException();
}
//Modified here to get automapper mappings
var maps = Mapper.FindTypeMapFor<TOldTarget, TNewTarget>();
var mutator = new ExpressionTargetTypeMutator(t => typeof(TNewTarget), maps);
var explorer = new ExpressionTreeExplorer();
var converted = mutator.Visit(predicate.Body);
return Expression.Lambda<Func<TNewTarget, bool>>(
converted,
lambda.Name,
lambda.TailCall,
explorer.Explore(converted).OfType<ParameterExpression>());
}
protected override Expression VisitMember(MemberExpression node)
{
var dataContractType = node.Member.ReflectedType;
var activeRecordType = _typeConverter(dataContractType);
PropertyMap prop = null;
foreach (var propertyMap in _maps)
{
var source = propertyMap.SourceMember;
var dest = propertyMap.DestinationProperty;
if (source != null && source.Name == node.Member.Name)
{
prop = propertyMap;
}
}
if (prop == null)
{
return base.VisitMember(node);
}
var propertyName = prop.DestinationProperty.Name;
var property = activeRecordType.GetProperty(propertyName);
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
property
);
return converted;
}
The problem is, my entity object doesn't have all of the same properties as my Model object (Account object versus AccountId for example). When the Transformer gets to the Account property on the Model object, I get an Exception (because there is no matching property on my Entity object). I cannot return null from VisitMember, and new Expression() is not allowed either. How can I handle ignoring properties on my Model object that do not exist on my Entity object?
Updating with info from Comments
So, to be a little more clear, I am using Automapper to map from a Models.Store to an Entity.Store. My entity.Store only has an AccountId (because I don't want to duplicate all of the account data), but my Models.Store needs the whole account object (which I would get by querying the Accounts collection).
Automapper is bascially converting my Account object to just an AccountId on my entity. Therefore, when I search for x => x.Account.AccountId == abcd1234 (where x is a models.Store), I need my expression to convert to x => x.AccountId == abcd1234 (where x is an Entity.Store).
I have that part working (changing mS => mS.Account.AccountId == 1234 to mE => mE.AccountId == 1234). The problem I am having now is that after doing the AccountId property, VisitMember is called with Account as the node. Since there is no Account in my Entity.Store object, I get the exception.
It's rather hard to test a solution without testable/runnable code. But here's a guess
Given the following expression mS => mS.Account.AccountId == 1234 and looking to transform MemberExpressions, you'll get the following calls:
VisitMember(mS.Account.AccountId
VisitMember(mS.Account)
You want to transform the second one into mE.AccountId. This involves two transformations: One, changing the property access from (EntityType).(AccountType).AccountId to (MongoStoreType).AccountId, and also changing the underlying object. If you're already handling the parameter transformations in other methods of your ExpressionVisitor, probably VisitParameter and VisitLambda, you'll be fine there. You then just need to skip looking at the parent MemberAccess, and jump straight to the grandparent:
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
property
);
return converted;
becomes something like this:
var parentMember = node.Expression as MemberExpression;
if (parentMember != null)
{
var grandparent = parentMember.Expression;
var converted = Expression.MakeMemberAccess(
base.Visit(grandparent),
property
);
return converted;
}
else
{
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
property
);
return converted;
}
Is it possible to select a single object and populate a containing IEnumerable property with a single Lambda expression?
Something like this:
var someViewModel = _repository.Table.Where(x => x.Id == someId)
.Select(new ListViewModel(){
GroupId = x.Group.Id,
GroupTitle = x.Group.Title
List = ?? // Select new SubViewModel and add it to IEnumerable<SubViewModel>
})
The result I'm after is a new object (ListViewModel in this case) that contains 3 properties. "List" being a collection of newly selected objects.
Is this possible? Am I coming at this from the wrong angle?
Thanks!
Update:
Let me try again :) Keep in mind that my naming is fictional here. Given the following two classes I would like to construct a DB query using a Lambda expression which creates a single "ListViewModel" that contains a collection of "SubViewModel". Does this help clarify?
public class SubViewModel
{
public int Id { get; set; }
public string Title { get; set; }
}
public class ListViewModel
{
public int GroupId { get; set; }
public string GroupTitle { get; set; }
public IEnumerable<SubViewModel> List { get; set; }
}
I'm not sure if I understand your question correctly but here is what I am thinking, you need to create a new IEnumberable and add the item to that collection.
var someViewModel = _repository.Table.Where(x => x.Id == someId)
.Select(new ListViewModel()
{
GroupId = x.Group.Id,
GroupTitle = x.Group.Title
List = new List<SubViewModel> { new SubViewModel(x) }
});
Ugh, how do I explain this one... Probably a simple question but my mind is fried.
Suppose I have this class:
public class NestedObject
{
public string NestedName { get; set; }
public int NestedIntValue { get; set; }
public decimal NestedDecimalValue { get; set; }
}
public class SomeBigExternalDTO
{
public int Id { get; set; }
public int UserId { get; set; }
public int SomeIntValue { get; set; }
public long SomeLongValue { get; set; }
public decimal SomeDecimalValue { get; set; }
public string SomeStringValue { get; set; }
public NestedObject SomeNestedObject { get; set; }
// ... thousands more of these properties... inherited code
}
And the class I'd like to populate is here:
public class MyResult
{
public int UserId { get; set; } // user id from above object
public string ResultValue { get; set; } // one of the value fields from above with .ToString() executed on it
}
What I'd like to do is create a helper to return the property values (a cross section is the best way I could describe it I guess) of all instances in a list of this object:
var foo = new List<SomeBigExternalDTO>();
foo = GetMyListOfSomeBigExternalDTO();
public static List<MyResult> AwesomeHelper(List<SomeBigExternalDTO> input, SearchableProperty thePropertyIWant)
{
// some magic needs to happen here...
}
The tricky part here is I want to dynamically pass in the property based on a link selector (I have no clue how to do this):
var output = AwesomeHelper(GetMyListOfSomeBigExternalDTO(), x => x.SomeIntValue);
var output2 = AwesomeHelper(GetMyListOfSomeBigExternalDTO(), x => x.SomeNestedObject.NestedIntValue);
And this should return a list of MyResult objects with the UserId and SomeIntValue.ToString() corresponding to each item in the input list.
Wow, I really hope this makes sense. Please let me know if this is not clear I'll provide more details. I'm really hoping this is something baked into the libraries that I've overlooked.
Any ideas on I'd accomplish this?
You could implement it as an extension method:
public static IEnumerable<MyResult> AwesomeHelper(this IEnumerable<SomeBigExternalDTO> input,
Func<SomeBigExternalDTO, int> intMapper)
{
foreach (var item in input)
yield return new MyResult()
{
UserId = item.UserId,
ResultValue = intMapper(item)
};
}
Now you can use it like this:
var output = GetMyListOfSomeBigExternalDTO().AwesomeHelper( x => x.SomeIntValue);
var output2 = GetMyListOfSomeBigExternalDTO().AwesomeHelper( x => x.SomeNestedObject.NestedIntValue);
Having said that - dont' do that - it somehow looks like you are reinventing what Linq already offers you, you can do just the same using only Linq:
var output = GetMyListOfSomeBigExternalDTO().Select( x=> new MyResult()
{
UserId = item.UserId,
ResultValue = x.SomeIntValue
});
var output2 = GetMyListOfSomeBigExternalDTO().Select( x=> new MyResult()
{
UserId = item.UserId,
ResultValue = x.SomeNestedObject.NestedIntValue
});
Often when trying to create a general purpose list operator you end up reimplementing what LINQ already offers you.
Here's the LINQ code for what you're after (without an AwesomeHelper function):
var results = list.Select(l => new MyResult()
{
UserId = l.UserId,
ResultValue = l.SomeDecimalValue.ToString()
}).ToList();
Fairly simple.
If you want to have an AwesomeHelper function as you requested then it looks like this:
public static List<MyResult> AwesomeHelper(
List<SomeBigExternalDTO> input,
Func<SomeBigExternalDTO, object> selector)
{
return input
.Select(i => new MyResult()
{
UserId = i.UserId,
ResultValue = selector(i).ToString()
})
.ToList();
}
And the calling code look like this:
var results = AwesomeHelper(list, x => x.SomeIntValue);
To me, though, this is now less readable than the LINQ option. Now there is some magic being wrought and it's hard to work out what.
I have an alternative that will give you the best of both worlds.
First, define an extension method called ToMyResult that maps a single SomeBigExternalDTO instance into a single MyResult with a field selector, like this:
public static class AwesomeHelperEx
{
public static MyResult ToMyResult(
this SomeBigExternalDTO input,
Func<SomeBigExternalDTO, object> selector)
{
return new MyResult()
{
UserId = input.UserId,
ResultValue = selector(input).ToString()
};
}
}
Now the calling code is crystal clear, flexible and concise. Here it is:
var results = (
from item in list
select item.ToMyResult(x => x.SomeLongValue)
).ToList();
I hope this helps.