用于在SQL IN()中获得包含EF的LINQ的表达式,其中在实体上子级的属性等于值

时间:2019-03-21 23:19:43

标签: c# entity-framework linq entity-framework-core

我很简单地需要从返回的集合中过滤掉所有的父母,在该集合中,字段上没有匹配项,即从字符串中按名称调用,与给出的值不匹配。我所追求的是如果parent对象具有child对象,并且该child对象属性"foo"(由字符串调用)不等于值{{1 }},bar对象将从集合中适当过滤。

这是我的linq ef通话

parent

我希望看到的是类似SQL的

var field = "bar";
var values = new List<string>{"foo","fuYu"};
var dataPage = _aim_context.ae_s_bld_c.AsNoTracking();
var result = dataPage.Where(x => 
                              DbHelper.byPropertyContains(x.udfs, field, values)
                           );
// NOTE `udfs` is a ONE-to-ONE with `ae_s_bld_c`

我解决此问题的方法是设置一个表达式以使用SELECT [m].[id],[m.udfs].[bar], FROM [dbo].[ae_s_bld_c] AS [m] INNER JOIN [dbo].[ae_s_bld_c_udf] AS [m.udfs] ON ([m].[multitenant_id] = [m.udfs].[multitenant_id]) WHERE ([m].[multitenant_id] = 1.0) AND ([m.udfs].[bar] IN ('foo','fuYu')) --< Goal line 并编写SQL。我已经阅读了近50篇文章和SO帖子,但是由于每个人似乎都有不同的想法,所以还没有弄清楚我为什么还没有明白这一点,而且似乎大多数人都不符合dotnet core 2.1 +。

经过很多次迭代之后,我现在坐在这里。 注意:与我追求的目标有些不同。

我当前的环境linq尝试

List<string>

我认为最好是像我举的第一个示例那样,但这是我一直坚持的,因为我有一段时间将其与//... dataPage = dataPage.Where(DbHelper.byPropertyContains<ae_s_bld_c>("udfs", field, values)); //... 对齐,两者都为{{1} }和x=>x.udfs

构建表达式的静态方法

x=> funName(x.udfs)

更新

根据@NetMage的请求,我尝试向后使用LINQpad。我想我很亲密,但输出很难告诉。我把它放在这里供参考。明确地说,子级的属性名称将是名称的字符串。最好的结果是我可以有一个像x=> x.udfs.funName()这样的名称,如果值包含字符串名,我可以在任何级别进行测试,但是从这里开始确实可以,

public static class DbHelper
{
    public static Expression<Func<T, bool>> byPropertyContains<T>(string node, string field, List<string> value) {
//trying to take parent item and get it's property by string name because
// doing the function in linq like x=>x.udfs was not working right
// but that is the prefered I think
        var property_parameter = Expression.Parameter(typeof(T), "x");
        var property = Expression.PropertyOrField(property_parameter, node);
        var selector_parameter = Expression.Parameter(property.Type, "y");
        var selector = Expression.PropertyOrField(selector_parameter, field);
        var methodInfo = typeof(List<string>).GetMethod("Contains", new Type[] {
            typeof(string)
        });
        var list = Expression.Constant(value, typeof(List<string>));
        var body = Expression.Call(methodInfo, list, selector);
        return Expression.Lambda<Func<T, bool>>(body, selector_parameter);
    }
}

output of LINQpad

1 个答案:

答案 0 :(得分:1)

从这里开始。您需要类似这样的东西

field

其中var result = dataPage .Where(x => values.Contains(EF.Property<string>(x.udfs, field))); 是按名称动态指定的字符串返回属性。

在EF Core中,您甚至不需要手动处理构建表达式,因为EF Core提供了一种特殊的SQL可翻译函数,用于通过名为EF.Property的名称访问简单属性。

使用该方法,解决方案很简单:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Configuration
@Order(2)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${security.signing-key}")
    private String signingKey;

    @Value("${security.encoding-strength}")
    private Integer encodingStrength;

    @Value("${security.security-realm}")
    private String securityRealm;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable().authorizeRequests().antMatchers("/api/**").hasRole("admin").and().httpBasic()
                .authenticationEntryPoint(authenticationEntryPoint());
    }

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
        entryPoint.setRealmName(securityRealm);
        return entryPoint;
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(signingKey);
        return converter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    @Primary 
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }
}

@Configuration
@Order(1)
public static class FormWebSecurityConfig extends WebSecurityConfigurerAdapter {
    private static final String LOGIN_PROCESSING_URL = "/login";
    private static final String LOGIN_FAILURE_URL = "/login?error";
    private static final String LOGIN_URL = "/login";
    private static final String LOGOUT_SUCCESS_URL = "/logout";
    private static final String LOGIN_SUCCESS_URL = "/user";


    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable() 
                .authorizeRequests() 
                .antMatchers("/user").hasAnyAuthority(Role.getAllRoles()).anyRequest().authenticated().and()                                                                
                .formLogin().loginPage("/login").permitAll().and() 
                .logout().permitAll();
    }


    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(

                //...)
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public User currentUser(UserRepository userRepository) {
        return userRepository.findByEmailIgnoreCase(SecurityUtils.getUsername());
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

    @Bean()
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

}
}