Home > OS >  LINQ C# FirstOrDefault() query return null for the existing element
LINQ C# FirstOrDefault() query return null for the existing element

Time:03-31

I want to perform some simple queries for these below models using EF Core code first.

Post model

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public int FirstTagId { get; set; }
    public FirstTag FirstTag { get; set; }
    public int SecondTagId { get; set; }
    public SecondTag SecondTag { get; set; }
}

FirstTag model

public class FirstTag
{
    public int Id { get; set; }
    public string tagName { get; set; }
    public ICollection<Post> Post { get; set; }
}

SecondTag model

public class SecondTag
{
    public int Id { get; set; }
    public string tagName { get; set; }
    public ICollection<Post> Post { get; set; }
}

My intention is that when I create a new post. I want to check that does the tag will be inserted into the new post exists or not using the below check operation

[HttpPost]
public async Task<ActionResult<Post>> CreatePost([FromBody] NewPostParam postParam)
{
    // to prevent inserting duplicate first tag
    var tag1 = _context.FirstTags.Where(x =>
        (string.IsNullOrEmpty(postParam.Post.FirstTag.tagName) || x.tagName == postParam.Post.FirstTag.tagName))
        .FirstOrDefault();
    // to prevent inserting duplicate second tag 
    var tag2 = _context.SecondTags.Where(x =>
        (string.IsNullOrEmpty(postParam.Post.SecondTag.tagName) || x.tagName == postParam.Post.SecondTag.tagName))
        .FirstOrDefault();

    if (tag1 == null)
    {
        tag1 = new FirstTag
        {
            tagName = "tag1 test",
            tagDescription = "tag1 test des!!"
        };
        _context.FirstTags.Add(tag1);
    }
    if (tag2 == null)
    {
        tag2 = new SecondTag
        {
            tagName = "tag2 test",
            tagDescription = "tag2 test des!"
        };
        _context.SecondTags.Add(tag2);
    }
     var newPost = new Post
     {
        Title = postParam.Post.Title,
        Description = postParam.Post.Description,
        FirstTag = tag1 != null ? tag1 : postParam.Post.FirstTag,
        SecondTag = tag2 != null ? tag2 : postParam.Post.SecondTag,
     };

    _context.Posts.Add(newPost);
    // remain code ...
}

This is the currently existing tags database

FirstTag table

Id  |  tagName
1     firstTag1
2     firstTag2

SecondTag table

Id  |  tagName 
1      secondTag1
2      secondTag2

And this is my JSON data for adding new post

{
    "post": {
        "title": "post2 test titlte",
        "description": "post2 test description!",
         "firstTag": {
            "tagName": "firstTag1",
            "tagDescription": "firstTag des!!"
        },
        "secondTag": {
            "tagName": "secondTag2",
            "tagDescription": "secondTag des!!"
        }
    }
}

I've tried several times to add the same name for FirstTag and SecondTag. But the new data is just created and the check operation I performed above maybe did nothing to prevent that. What do I do to prevent duplicate data from being inserted?

CodePudding user response:

Unless you did not post something from your data model, FirstTag and SecondTag should be the same Model, or at the very least inherit from the same base model.

Your class Post does not contain the definition for SecondTag but I'll assume it's the same as FirstTag. You're also missing Id in your JSON example.

Ignoring those inconsistencies, you're first defining 2 variables in your CreatePost: tag1 and tag2. Since postParam will not have a filled tag, both of these variables will initialize as null. As such, it's logical that both if statements will validate as true

So, what you want to do is more along the lines of:

[HttpPost]
public async Task<ActionResult<Post>> CreatePost([FromBody] NewPostParam postParam)
{
    // to prevent inserting duplicate first tag
    var tag1 = _context.FirstTags.Where(x =>
        (string.IsNullOrEmpty(postParam.Post.FirstTag.tagName) || x.tagName == postParam.Post.FirstTag.tagName))
        .FirstOrDefault();
SecondTag tag2 = null;

if (tag1 == null)
{
    tag1 = new FirstTag
    {
        tagName = "tag1 test",
        tagDescription = "tag1 test des!!"
    };
    _context.FirstTags.Add(tag1);
}
else
{
   tag2 = _context.SecondTags.Where(x =>
        (string.IsNullOrEmpty(postParam.Post.SecondTag.tagName) || x.tagName == postParam.Post.SecondTag.tagName))
        .FirstOrDefault();

   if (tag2 == null)
   {       
       tag2 = new SecondTag
       {
           tagName = "tag2 test",
           tagDescription = "tag2 test des!"
       };
       _context.SecondTags.Add(tag2);
   }
}
 var newPost = new Post
 {
    Title = postParam.Post.Title,
    Description = postParam.Post.Description,
    FirstTag = tag1 != null ? tag1 : postParam.Post.FirstTag,
    SecondTag = tag2 != null ? tag2 : postParam.Post.SecondTag,
 };

_context.Posts.Add(newPost);
// remain code ...

}

Secondly, you're not checking whether tag1 already has a value like tag2. If you NEVER want tag2 to be filled with a tag of tag1, do something like this:

tag2 = _context.SecondTags.Where(x =>
            string.IsNullOrEmpty(postParam.Post.SecondTag.tagName) ||  (x.tagName == postParam.Post.SecondTag.tagName && postParam.Post.FirstTag.tagName != x.tagName) 
            .FirstOrDefault());

CodePudding user response:

Ignoring some questionable aspects of your design, try this:

if(!string.IsNullOrEmpty(postParam.Post.FirstTag.tagName))
{
    if(!_context.FirstTags.Any(t => t.tagName.Trim().ToLower() == postParam.Post.FirstTag.tagName.Trim().ToLower()))
    {
        var tag1 = new FirstTag
        {
            tagName = "tag1 test",
            tagDescription = "tag1 test des!!"
        };
        _context.FirstTags.Add(tag1);
    }
}

if (!string.IsNullOrEmpty(postParam.Post.SecondTag.tagName))
{
    if (!_context.FirstTags.Any(t => t.tagName.Trim().ToLower() == postParam.Post.SecondTag.tagName.Trim().ToLower()))
    {
        var tag2 = new FirstTag
        {
            tagName = "tag1 test",
            tagDescription = "tag1 test des!!"
        };
        _context.SecondTags.Add(tag2);
    }
}

As you can see, I separated validation of the model value from the query. I personally dislike convoluted one-liners that make things hard to understand.

The code checks if there is match, taking into account padding and casing (since I don't know much about your backend).

Back to the questionable design. Notice how the code is suspiciously repetitive? (Hint: It has to do with the fact that a tag is a tag, regardless of its order or how many there are.)

CodePudding user response:

You can check for duplicate records as follows

var isDuplicate = _context.FirstTags.Any(x =>x.tagName == postParam.Post.FirstTag.tagName)
if(isDuplicate){
  //Do something
}
  • Related