问题
I have a around 50 databases that have 150 tables each and working on a search mechanism that would allow to query the tables that have specific columns. Most database structures are similar so idea was to generate EF entities and put interfaces behind the entities generated if tables that they are being generated from have specific columns, so that I could later query then on that column.
After doing this I ran into unexpected problem on some tables column I am trying to map is Long while on others it's Decimal (oracle) and sometimes it's Nullable and sometimes it's not.
One solution I can see to this is to add interface without any properties and when at runtime determine specific type of that column, before running the query.
Is there a way to add some type on interface for property that might be more than one type so compiler wouldn't complain about not being implemented? object does not work, let me know if you need code examples as question is somewhat complicated.
Mechanism I intend to use on entities:
public static class ExpressionExtensions
{
public static IQueryable<T> ApplySearch<T>(this IQueryable<T> queryable, SearchModel search) where T : class
{
if (search != null && search.PolicyNumber.HasValue && typeof(IPolicyNumber).IsAssignableFrom(queryable.ElementType))
{
queryable = queryable.SearchByPolicyNumber(search);
}
return queryable;
}
public static IQueryable<IPolicyNumber> SearchByPolicyNumber<IPolicyNumber>(this IQueryable<IPolicyNumber> queryable, SearchModel search)
{
var policyNumberParameterLambda = Expression.Parameter((typeof(IPolicyNumber)));
var policyNumberColumnLambda = Expression.Property(policyNumberParameterLambda, "POLICY_NO");
var lambda = Expression.Lambda<Func<IPolicyNumber, bool>>(
Expression.Equal(policyNumberColumnLambda,
Expression.Convert(Expression.Constant(search.PolicyNumber), policyNumberColumnLambda.Type)
), policyNumberParameterLambda);
return queryable.Where(lambda);
}
}
回答1:
figured it out myself
Interface that gets put on entities has to change from this
public interface IUniqueId
{
long UNIQUE_ID { get; set; }
}
to this
public interface IUniqueId<T> : IUniqueId
{
T UNIQUE_ID { get; set; }
}
public interface IUniqueId
{
}
and then where interface is put on entity
from
public partial class QUOTE_HOUSE : IUniqueId
{
public long UNIQUE_ID { get; set; }
...
}
to
public partial class QUOTE_HOUSE : IUniqueId<long>
{
public long UNIQUE_ID { get; set; }
....
}
and T4 code that generates interfaces changed from
public string EntityClassOpening(EntityType entity)
{
var stringsToMatch = new Dictionary<string,string> { { "POLICY_NO", "IPolicyNumber" }, { "UNIQUE_ID", "IUniqueId" } };
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}{4}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)),
stringsToMatch.Any(o => entity.Properties.Any(n => n.Name == o.Key)) ? " : " + string.Join(", ", stringsToMatch.Join(entity.Properties, l => l.Key, r => r.Name, (l,r) => l.Value)) : string.Empty);
}
to
public string EntityClassOpening(EntityType entity)
{
var stringsToMatch = new Dictionary<string,string> { { "POLICY_NO", "IPolicyNumber" }, { "UNIQUE_ID", "IUniqueId" } };
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}{4}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)),
stringsToMatch.Any(o => entity.Properties.Any(n => n.Name == o.Key)) ? " : " + string.Join(", ", stringsToMatch.Join(entity.Properties, l => l.Key, r => r.Name, (l,r) => string.Format("{0}<{1}{2}>", l.Value, r.TypeUsage.EdmType.Name, r.Nullable && r.TypeUsage.EdmType.Name!="String" ? "?" : ""))) : string.Empty);
}
Generics FTW indeed...
Hope this saves you some time.
来源:https://stackoverflow.com/questions/37812584/putting-interface-behind-properties-with-same-name-but-different-types