简体   繁体   中英

Using a Generic Function to query a DBSet in EF5

I have seen various implementations of DbSet but I'm not sure they are what I am looking for.

Basically we have a bunch of DbSet's set up through EF5 Code First approach. I have a task of writing code to check any updates/posts coming into the API against the records we already have in the DB to make sure they only are updated/posted if they are new records.

Obviously I don't want to write the same code for each of these DBSet's, I would like to write a single class/function that will be able to take in a DbSet as an argument/parameter and then check that DBSet for the record. It needs to be dynamic because it is dependant on the end user and what they are doing as to which DbSet will need to be queried...

So for instance I want to do something like call:

CheckDbUniqueRecord(Contracts) where Contracts is a DbSet of a Contract model that is being passed in from a form.

So In the model we have public class Contract

and in the MainDbContext we have multiple DbSet's one of which is public DbSet<Contract> Contracts {get; set;} public DbSet<Contract> Contracts {get; set;}

I want to be able to run the same code to check for a duplicate record on any of the DbSets we have by simply passing in the class/model that is being returned from that particular controller with that one line function...

I know there is a way to accomplish this but I keep getting error messages/type errors from C# saying the types don't match, etc...

any help would be greatly appreciated.

UPDATE: I will only be needing this to check against duplicate records in the DB, not for updates. I will handle the checking updates on the client side before calling the Put function.

I have the following code which is very nearly working:

public interface getId
        {
            int Id { get; set; }
        }
        public class Check : getId
        {
            public int Id { get; set; }

            public static bool CheckDBUniqueRecord<T>(T entity) where T : class, getId
            {
                MainDbContext db = new MainDbContext(Utility.PortalConnectionString());

                var myDbSet = db.Set<T>().ToList();

                foreach (var d in myDbSet)
                {
                    d.Id = 0;
                    if (d == entity) return true;

                }
                return false;


            }
        }

By using a generic constraint, however I am getting the error on the method call: The Type xxxx cannot be used as type parameter 'T' in the generic method yyyyyy. There is no implicit reference conversion from xxxx to yyyy.

Whats the fix for this?

first thing first: all of this is just written down and untested...

the first thing is simple: your functions signature...

public static bool CheckDbUniqueRecord<T>(T entity)

or something similar like an extensionmethod ...

public static bool CheckDbUniqueRecord<T>(this DbContext db, T entity)

basically what you want to do is to query your DbContext for the DbSet that corresponds to your Entity ...

var myDbSet = db.Set<T>();

now you need some sort of logic to find out which properties you want to check for ... that can by done by placing attributes on your entity class members ... that can be done by checking everything that is not a primary key field ... you have to come up with something that tells you which things to test for ... for simplicity of this answer i'll just have some very simple thing that goes for every property with a getter that is not named "ID" ... depending on your implementation you will most likely want to move some of this code to an initialization code block that is not run every time...

var t = typeof(T);
var pInfos = t.GetProperties(BindingFlags.Public|BindingFlags.Instance).Where(x=>x.Name!="ID" && x.CanRead).ToArray();
Expression exp=null;
Expression pT = Expression.Parameter(t);
foreach(var p in pInfos)
{
Expression m = Expression.Property(pT,p);
Expression c = Expression.Constant(p.GetValue(entity));
if(tmp==null)
{
tmp=Expression.Equal(m,c);
}
else
{
tmp=Expression.AndAlso(tmp,Expression.Equal(m,c));
}
}
var myLambda=Expression.Lambda<Func<T,bool>>(tmp,pT);


return myDbSet.AsQueryable().Any(myLambda);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM