C# 添加LINQ或DBContext扩展方法以获取元素(如果不存在),然后使用谓词中的数据创建(FirstOrCreate)

C# 添加LINQ或DBContext扩展方法以获取元素(如果不存在),然后使用谓词中的数据创建(FirstOrCreate),c#,linq,entity-framework,extension-methods,C#,Linq,Entity Framework,Extension Methods,我试图添加一个LINQ或DbContext扩展方法来获取元素(FirstOrDefault),但如果还不存在,则使用数据创建一个新实例(FirstOrCreate),而不是返回null 这可能吗 i、 e: public static class LINQExtension { public static TSource FirstOrCreate<TSource>( this IEnumerable<TSource> source,

我试图添加一个LINQ或DbContext扩展方法来获取元素(FirstOrDefault),但如果还不存在,则使用数据创建一个新实例(FirstOrCreate),而不是返回null

这可能吗

i、 e:

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 // ??? 
        }
    }
}
我希望你能理解我的方法

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();
    }
}
但是,您必须注意,这与
FirstOrDefault()
的工作方式不同

如果你做了以下事情

var listOfStuff = new List<string>() { "Enabled" };
var statuses = from s in listOfStuff
               select db.EntitiesStatus.FirstOrCreate(s => s.Name == "Enabled", 
                        () => new EntityStatus {Name = "Enabled"});
var listofsuff=new List(){“Enabled”};
var STATUS=来自列表中的s
选择db.entitiestatus.FirstOrCreate(s=>s.Name==“已启用”,
()=>newentitystatus{Name=“Enabled”});
你会在数据库中找到O(n)个点击

但是我怀疑如果你做了

var listOfStuff = new List<string>() { "Enabled" };
var statuses = from s in listOfStuff
               select db.EntitiesStatus.FirstOrDefault(s => s.Name == "Enabled") 
                             ?? new EntityStatus {Name = "Enabled"};
var listofsuff=new List(){“Enabled”};
var STATUS=来自列表中的s
选择db.entitiestatus.FirstOrDefault(s=>s.Name==“已启用”)
?? 新实体状态{Name=“Enabled”};
这似乎是可行的…

结论: 最好的解决方案是使用??以这种方式:

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

我是一个自学成才的程序员,我打字很差,所以我一直在寻找完全相同的东西。最后我写了自己的。在使用多个属性之前,它采取了一些步骤和修改。当然也有一些限制,我还没有完全测试过它,但到目前为止,它似乎对我保持数据库中记录的清晰性和缩短代码(键入时间)起到了作用

扩展将首先检查数据库,如果找不到,它将检查本地上下文(因此您不会添加两次),如果仍然找不到,它将创建实体,解析表达式树以从lambda表达式获取属性和值,在新实体上设置这些值,将实体添加到上下文并返回新实体

有几件事需要注意

  • 这不会处理所有可能的使用(假设lambda中的所有表达式都是==)
  • 我在其中所做的项目是使用ObjectContext作为DBContext的替代(我还没有切换,所以我不知道这是否适用于DBContext。我认为这不难更改)
  • 我是自学成才的,所以可能有很多方法来优化这一点。如果你有任何意见,请让我知道

  • 这个扩展也将新创建的实体添加到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);
        }
    
    }
    
    或不带谓词:

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

    如果没有找到一个新对象,那么返回一个没有设置值的新对象是很容易的,但是您是否希望它以某种方式查看谓词并根据您所查找的内容设置新对象的属性?例如,在本例中,您是否希望它返回Name==“Enabled”的状态,感谢您的快速响应。。。对,这正是我想要的…但我需要对所有实体(不仅仅是状态)使用此扩展方法,所以应该以通用方式工作…可能使用反射?另外…如何从谓词中读取属性?这个问题的快速解决方案是使用??运算符:var status=db.entitiestatus.FirstOrDefault(s=>s.Name==“Enabled”)??新EntityStatus(){Name=“Enabled”};但还是想知道是否有可能用一种通用的扩展方法得到一些东西…@NanoSassaroli-mabless。但我建议不要这样做。谓词几乎可以包含任何东西;它不一定只有属性。也可能有方法、其他lambda表达式等,因此没有简单的方法来解析您要设置的属性的谓词。Aron,感谢您的贡献,在此之后,我可以断定,简单/最有效的方法是使用??运算符,而不是实现/使用扩展方法。'var status=db.entitiestatus.FirstOrDefault(s=>s.Name==“已启用”)??新EntityStatus(){Name=“Enabled”};”“同意吗?
    var status = db.EntitiesStatus.InsertIfNotExists(s => s.Name == "Enabled");
    
    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);
        }
    
    }
    
    var adminUser = DbContext.Users.FirstOrCreate(u => u.Name == "Admin", () => new User { Name = "Admin" });
    
    var adminUser = DbContext.Users.FirstOrCreate(() => new User { Name = "Admin" });