When using a repository is it possible for a type to return a Func that the repository uses to test for existing entities?
For example given a Factory with a method
public static T Save<T>(T item) where T : Base, new()
{
/* item.Id == Guid.Empty therefore item is new */
if (item.Id == Guid.Empty && repository.GetAll<T>(t => t.Name == item.Name))
{
throw new Exception("Name is not unique");
}
}
how do I create a property of Base
(say MustNotAlreadyExist
) so that I can change the method above to
public static T Save<T>(T item) where T : Base, new()
{
/* item.Id == Guid.Empty therefore item is new */
if (item.Id == Guid.Empty && repository.GetAll<T>(t.MustNotAlreadyExist))
{
throw new Exception("Name is not unique");
}开发者_高级运维
}
public class Base
{
...
public virtual Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name); /* <- this clearly doesn't work */
}
}
and then how can I override MustNotAlreadyExist in Account : Base
public class Account : Base
{
...
public override Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name && b.AccountCode == accountCode); /* <- this doesn't work */
}
...
}
Try this:
public class Account : Base
{
...
public override Expression<Func<T, bool>> MustNotAlreadyExist()
{
return (b => b.Name == name && b.AccountCode == accountCode).Any();
}
...
}
The Any() method will return true if any record matches the predicate. It could be argued that it is outside the responsibility of the repository to check for presence of a record before saving.
UPDATE:
There is a great article on CodeProject that describes a generic Repository for Entity Framework:
http://www.codeproject.com/KB/database/ImplRepositoryPatternEF.aspx
This could be applied to a non-Entity Framework data context. Here is an excerpt that provides a very flexible method for checking for an existing value by accepting the name of a field, a value, and a key value. You can apply this to any Entity type and use it to check for the presence of an entity before attempting a save.
/// <summary>
/// Check if value of specific field is already exist
/// </summary>
/// <typeparam name="E"></typeparam>
/// <param name="fieldName">name of the Field</param>
/// <param name="fieldValue">Field value</param>
/// <param name="key">Primary key value</param>
/// <returns>True or False</returns>
public bool TrySameValueExist(string fieldName, object fieldValue, string key)
{
// First we define the parameter that we are going to use the clause.
var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
MemberExpression leftExprFieldCheck =
MemberExpression.Property(xParam, fieldName);
Expression rightExprFieldCheck = Expression.Constant(fieldValue);
BinaryExpression binaryExprFieldCheck =
MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
MemberExpression leftExprKeyCheck =
MemberExpression.Property(xParam, this._KeyProperty);
Expression rightExprKeyCheck = Expression.Constant(key);
BinaryExpression binaryExprKeyCheck =
MemberExpression.NotEqual(leftExprKeyCheck, rightExprKeyCheck);
BinaryExpression finalBinaryExpr =
Expression.And(binaryExprFieldCheck, binaryExprKeyCheck);
//Create Lambda Expression for the selection
Expression<Func<E, bool>> lambdaExpr =
Expression.Lambda<Func<E, bool>>(finalBinaryExpr,
new ParameterExpression[] { xParam });
//Searching ....
return ((IRepository<E, C>)this).TryEntity(new Specification<E>(lambdaExpr));
}
/// <summary>
/// Check if Entities exist with Condition
/// </summary>
/// <param name="selectExpression">Selection Condition</param>
/// <returns>True or False</returns>
public bool TryEntity(ISpecification<E> selectSpec)
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Any<E>
(selectSpec.EvalPredicate);
}
I am not shure if you problem is solvable since you need to access both the repository and the new item to be checked. The new item to be checked is not available in a seperate method.
However you can outsource the call to GetAll so that your code becomes something similar to (not tested)
public static T Save<T>(T item) where T : Base, new()
{
if (item.Id == Guid.Empty && (Check(repository, item)))
{
throw new Exception("Name is not unique");
}
}
public class Base
{
...
public Func<Enumerable<T>, T, bool> Check { get; set;}
public Base()
{
Check = (col, newItem) => (null != col.FirstOrDefault<T>(
item => item.Name == newItem.Name));
}
}
OK, here is the answer, this is a combination of the code posted by Dave Swersky and a little but of common sense.
public interface IUniqueable<T>
{
Expression<Func<T, bool>> Unique { get; }
}
public class Base, IUniqueable<Base>
{
...
public Expression<Func<Base, bool>> Unique
{
get
{
var xParam = Expression.Parameter(typeof(Base), typeof(Base).Name);
MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "Name");
Expression rightExprFieldCheck = Expression.Constant(this.Name);
BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
return Expression.Lambda<Func<Base, bool>>(binaryExprFieldCheck, new ParameterExpression[] { xParam });
}
}
...
}
public class Account : Base, IUniqueable<Account>
{
...
public new Expression<Func<Account, bool>> Unique
{
get
{
var xParam = Expression.Parameter(typeof(Account), typeof(Account).Name);
MemberExpression leftExprNameCheck = MemberExpression.Property(xParam, "Name");
Expression rightExprNameCheck = Expression.Constant(this.Name);
BinaryExpression binaryExprNameCheck = MemberExpression.Equal(leftExprNameCheck, rightExprNameCheck);
MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, "AccountCode");
Expression rightExprFieldCheck = Expression.Constant(this.AccountCode);
BinaryExpression binaryExprFieldCheck = MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
BinaryExpression binaryExprAllCheck = Expression.OrElse(binaryExprNameCheck, binaryExprFieldCheck);
return Expression.Lambda<Func<Account, bool>>(binaryExprAllCheck, new ParameterExpression[] { xParam });
}
}
...
}
public static class Manager
{
...
public static T Save<T>(T item) where T : Base, new()
{
if (!item.IsValid)
{
throw new ValidationException("Unable to save item, item is not valid", item.GetRuleViolations());
}
if (item.Id == Guid.Empty && repository.GetAll<T>().Any(((IUniqueable<T>)item).Unique))
{
throw new Exception("Item is not unique");
}
return repository.Save<T>(item);
}
...
}
Essentially by implementing the IUniqueable
interface for a specific type I can return a different Expression
for each type. All good :-)
精彩评论