#c# #sql-server #sqlbulktools
Вопрос:
Я пытаюсь создать универсальный sqlbulktools
метод, чтобы он мог использовать любую модель в качестве входных данных. Вот основной метод:
public void InsertOrUpdateTableMatchOnString<T>(string tableName, List<T> models) where T : class, IModel
{
BulkOperations bulk = new BulkOperations();
using (TransactionScope trans = new TransactionScope())
{
using (SqlConnection conn = new SqlConnection(MyConString))
{
bulk.Setup<T>()
.ForCollection(models)
.WithTable(tableName)
.AddAllColumns()
.BulkInsertOrUpdate()
.SetIdentityColumn(x => x.Id)
.MatchTargetOn(x => x.AgreementId)
.MatchTargetOn(x => x.GetMatchOn())
.Commit(conn);
}
trans.Complete();
}
}
Вот пример модели, которая используется в качестве входных данных:
public class Company : IModel
{
public long? Id { get; set; }
public string Name { get; set; }
public string BaseCurrency { get; set; }
public string Address1 { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string TelephoneNumber { get; set; }
public string Contact { get; set; }
public string WebSite { get; set; }
public string Email { get; set; }
public string CINumber { get; set; }
public DateTime SignUpDate { get; set; }
public int AgreementId { get; set; }
public PropertyInfo GetMatchOn()
{
return this.GetType().GetProperties().First(p => p.Name == "Name");
}
public Company SetProperties(CompanyData data, Customer customer)
{
Name = data.Name;
BaseCurrency = data.BaseCurrencyHandle.Code;
Address1 = data.Address1;
PostalCode = data.PostalCode;
City = data.City;
Country = data.Country;
TelephoneNumber = data.TelephoneNumber;
Contact = data.Contact;
WebSite = data.WebSite;
Email = data.Email;
CINumber = data.CINumber;
SignUpDate = data.SignUpDate;
AgreementId = customer.ecid;
return this;
}
}
И вот интерфейс, который реализует модель:
public interface IModel
{
long? Id { get; set; }
int AgreementId { get; set; }
PropertyInfo GetMatchOn();
}
Проблема в том , что я не могу использовать GetMatchOn()
метод в .MatchTargetOn(x => x.GetMatchOn())
вызове метода — он будет принимать только такое свойство, как .MatchTargetOn(x => x.AgreementId)
, иначе я получу ошибку. Но мне нужно, чтобы это был метод, так как это не одно и то же свойство от модели к модели, которое я хочу сопоставить. Возможно ли это каким-либо образом?
Комментарии:
1. Чтобы сделать это эффективно , вам нужна перегрузка
MatchTargetOn
, которая занимает aPropertyInfo
, чтобы обеспечить динамическое использование. Вы также можете рассмотреть перегрузку, которая просто принимает имя свойства как astring
и эффективно реализует то, чтоGetMatchOn
происходит сейчас (кстати, использованиеnameof
там лучше, чем жестко закодированный литерал), поскольку она сохраняет проверку, котораяPropertyInfo
действительно относится к свойству рассматриваемого класса (а не к какому-либо свойству другого класса).2. Обратите внимание, что оптимизацию динамического свойства доступ (который вам может понадобиться, когда вы копируете много и много строк), то ли сделать через имя,
PropertyInfo
илиExpression
,- это его собственная задача; сост делегатов простой, но не проанализировать, а также динамический метод, который имеет доступ ко всем свойствам в типизированном образе (хотя производя этот метод значительно сложнее получить право).3. Спасибо, что так быстро ответили 🙂 Извините, если я вас неправильно понял, но, похоже, вы думаете, что я сам могу сделать перегрузку MatchTargetOn, но SqlBulkTools — это пакет, который я использую, поэтому я не могу изменить эту часть кода-означает ли это, что это невозможно сделать?
4. Это все еще можно сделать, если нет перегрузки, но менее удобно.
MatchTargetOn
примет либо aFunc<T, TProp>
, либо anExpression<Func<T, TProp>>
для указания свойства; вы можете построить любой из них динамически, хотя это совсем не красиво, на самом деле, от задействованного кода у меня немного болит голова. Вы могли бы рассмотреть возможность инвертирования ответственности и передачи любого типа.BulkInsertOrUpdate()
в методIModel
, чтобы сам тип мог вызывать.MatchTargetOn()
. По общему признанию, это, вероятно, также позволит ему вызывать методы, которые он не должен, но это проще.
Ответ №1:
Спасибо, что указали мне правильное направление, Йерун Мостерт. Что в итоге сработало для меня, так это добавление метода в модели — fx, подобного этому:
public Expression<Func<Company, object>> GetMatchOn()
{
ParameterExpression parameter = Expression.Parameter(typeof(Company));
MemberExpression property = Expression.Property(parameter, "Name");
UnaryExpression convertedProperty = Expression.Convert(property, typeof(object));
Expression<Func<Company, object>> lambda = Expression.Lambda<Func<Company, object>>(convertedProperty, parameter);
return lambda;
}
а затем вызываем его из основного метода следующим образом:
public void InsertOrUpdateTableMatchOnString<T>(string tableName, List<T> models) where T : class, IModel<T, object>
{
BulkOperations bulk = new BulkOperations();
using (TransactionScope trans = new TransactionScope())
{
using (SqlConnection conn = new SqlConnection(MyConString))
{
bulk.Setup<T>()
.ForCollection(models)
.WithTable(tableName)
.AddAllColumns()
.BulkInsertOrUpdate()
.SetIdentityColumn(x => x.Id)
.MatchTargetOn(x => x.AgreementId)
.MatchTargetOn(models.First().GetMatchOn())
.Commit(conn);
}
trans.Complete();
}
}