Add a LINQ or DBContext extension method to get the item if it does not exist, then create the data in the predicate (FirstOrCreate)

I'm trying to add a LINQ or DbContext extension method to get an element (FirstOrDefault), but if not already, create a new instance with the data (FirstOrCreate) instead of returning null.

Is it possible?

ie:

public static class LINQExtension { public static TSource FirstOrCreate<TSource>( this IEnumerable<TSource> source, Func<TSource, bool> predicate) { if (source.First(predicate) != null) { return source.First(predicate); } else { return // ??? } } } 

and use may be:

 using (var db = new MsBoxContext()) { var status = db.EntitiesStatus.FirstOrCreate(s => s.Name == "Enabled"); //Here we should get the object if we find one //and if it doesn't exist create and return a new instance db.Entities.Add(new Entity() { Name = "New Entity", Status = status }); } 

I hope you understand my approach.

+4
source share
4 answers

conclussion: instead of introducing an extension method, does the best solution use? operator in this way:

 var status = db.EntitiesStatus.FirstOrDefault(s => s.Name == "Enabled") ?? new EntityStatus(){Name = "Enabled"}; 
0
source
 public static class LINQExtension { public static TSource FirstOrCreate<TSource>( this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, Func<T> defaultValue) { return source.FirstOrDefault(predicate) ?? defaultValue(); } } 

Using

 var status = db.EntitiesStatus.FirstOrCreate(s => s.Name == "Enabled", () => new EntityStatus {Name = "Enabled"}); 

However, you should note that this will not work like FirstOrDefault() .

If you have done the following

 var listOfStuff = new List<string>() { "Enabled" }; var statuses = from s in listOfStuff select db.EntitiesStatus.FirstOrCreate(s => s.Name == "Enabled", () => new EntityStatus {Name = "Enabled"}); 

You will get O (n) images in the database.

However, I suspect you did ...

 var listOfStuff = new List<string>() { "Enabled" }; var statuses = from s in listOfStuff select db.EntitiesStatus.FirstOrDefault(s => s.Name == "Enabled") ?? new EntityStatus {Name = "Enabled"}; 

This might probably work ...

+2
source

I am a self-learning programmer and I am very poor at typing, so I was looking for the same thing. I finished writing my own. This required several steps and revisions before he works with more than 1 property. Of course, there are some limitations, and I have not fully tested them, but so far this works for my purposes of storing records in the database and reducing code (input time).

 public static class DataExtensions { public static TEntity InsertIfNotExists<TEntity>(this ObjectSet<TEntity> objectSet, Expression<Func<TEntity, bool>> predicate) where TEntity : class, new() { TEntity entity; #region Check DB entity = objectSet.FirstOrDefault(predicate); if (entity != null) return entity; #endregion //NOT in the Database... Check Local cotext so we do not enter duplicates #region Check Local Context entity = objectSet.Local().AsQueryable().FirstOrDefault(predicate); if (entity != null) return entity; #endregion ///********* Does NOT exist create entity *********\\\ entity = new TEntity(); // Parse Expression Tree and set properties //Hit a recurrsive function to get all the properties and values var body = (BinaryExpression)((LambdaExpression)predicate).Body; var dict = body.GetDictionary(); //Set Values on the new entity foreach (var item in dict) { entity.GetType().GetProperty(item.Key).SetValue(entity, item.Value); } return entity; } public static Dictionary<string, object> GetDictionary(this BinaryExpression exp) { //Recurssive function that creates a dictionary of the properties and values from the lambda expression var result = new Dictionary<string, object>(); if (exp.NodeType == ExpressionType.AndAlso) { result.Merge(GetDictionary((BinaryExpression)exp.Left)); result.Merge(GetDictionary((BinaryExpression)exp.Right)); } else { result[((MemberExpression)exp.Left).Member.Name] = exp.Right.GetExpressionVaule(); } return result; } public static object GetExpressionVaule(this Expression exp) { if (exp.NodeType == ExpressionType.Constant) return ((ConstantExpression)exp).Value; if (exp.Type.IsValueType) exp = Expression.Convert(exp, typeof(object)); //Taken From http://stackoverflow.com/questions/238413/lambda-expression-tree-parsing var accessorExpression = Expression.Lambda<Func<object>>(exp); Func<object> accessor = accessorExpression.Compile(); return accessor(); } public static IEnumerable<T> Local<T>(this ObjectSet<T> objectSet) where T : class { //Taken From http://blogs.msdn.com/b/dsimmons/archive/2009/02/21/local-queries.aspx?Redirected=true return from stateEntry in objectSet.Context.ObjectStateManager.GetObjectStateEntries( EntityState.Added | EntityState.Modified | EntityState.Unchanged) where stateEntry.Entity != null && stateEntry.EntitySet == objectSet.EntitySet select stateEntry.Entity as T; } public static void Merge<TKey, TValue>(this Dictionary<TKey, TValue> me, Dictionary<TKey, TValue> merge) { //Taken From http://stackoverflow.com/questions/4015204/c-sharp-merging-2-dictionaries foreach (var item in merge) { me[item.Key] = item.Value; } } } 

Use is as simple as:

 var status = db.EntitiesStatus.InsertIfNotExists(s => s.Name == "Enabled"); 

The extension will first check the database, if it is not found, it will check the local context (so you do not add it twice), if it is not already found, it creates an entity, analyzes the expression tree to get the properties and values ​​from the lambda expression, sets these values ​​to new object, adds the object to the context and returns a new object.

A few things to know about ...

  • This does not handle all possible uses (assuming all expressions in lambda ==)
  • The project that I did this uses the ObjectContext associated with the DBContext (I haven’t switched yet, so I don’t know if this will work with DBContext. I assume that it will not be difficult to change)
  • I am self-taught, so there may be many ways to optimize this. If you have an input, please let me know.
0
source

How about this extension, which also adds a newly created object to DbSet.

 public static class DbSetExtensions { public static TEntity FirstOrCreate<TEntity>( this DbSet<TEntity> dbSet, Expression<Func<TEntity, bool>> predicate, Func<TEntity> defaultValue) where TEntity : class { var result = predicate != null ? dbSet.FirstOrDefault(predicate) : dbSet.FirstOrDefault(); if (result == null) { result = defaultValue?.Invoke(); if (result != null) dbSet.Add(result); } return result; } public static TEntity FirstOrCreate<TEntity>( this DbSet<TEntity> dbSet, Func<TEntity> defaultValue) where TEntity : class { return dbSet.FirstOrCreate(null, defaultValue); } } 

Use with predicate:

 var adminUser = DbContext.Users.FirstOrCreate(u => u.Name == "Admin", () => new User { Name = "Admin" }); 

or without predicate:

 var adminUser = DbContext.Users.FirstOrCreate(() => new User { Name = "Admin" }); 
0
source

Source: https://habr.com/ru/post/1483697/


All Articles