Если вы предоставляете только CONNECT для базы данных, пользователь может подключиться, но не имеет других привилегий. Вы должны предоставить USAGE для пространств имен (схем) и SELECT для таблиц и представлений по отдельности, например:
GRANT CONNECT ON DATABASE mydb TO xxx;
-- This assumes you're actually connected to mydb..
GRANT USAGE ON SCHEMA public TO xxx;
GRANT SELECT ON mytable TO xxx;
В последних версиях PostgreSQL Вы можете предоставить разрешения для всех таблиц / представлений / и т. д. в схеме, используя одну команду, вместо того, чтобы вводить их по одному:
GRANT SELECT ON ALL TABLES IN SCHEMA public TO xxx;
Это влияет только на таблицы, которые уже были созданы. Более эффективно, вы можете автоматически иметь роли по умолчанию, назначенные новым объектам в будущем:
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO xxx;
Обратите внимание, что по умолчанию это повлияет только на объекты (таблицы), созданные пользователем, который выдал это команда: хотя она также может быть установлена для любой роли, членом которой является выдающий пользователь. Однако вы не выбираете привилегии по умолчанию для всех ролей, в которых вы участвуете, при создании новых объектов ... так что все еще есть некоторые недоработки. Если вы принимаете подход, согласно которому база данных играет роль-владелец, а изменения схемы выполняются в качестве этой роли-владельца, вам следует назначить привилегии по умолчанию для этой роли-владельца. ИМХО, все это немного сбивает с толку, и вам, возможно, придется поэкспериментировать, чтобы придумать функциональный рабочий процесс.
Чтобы избежать ошибок при длительных многостоловых изменениях, рекомендуется использовать следующий «автоматический» процесс для генерации требуемого GRANT SELECT
для каждого table / view:
SELECT 'GRANT SELECT ON ' || relname || ' TO xxx;'
FROM pg_class JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
WHERE nspname = 'public' AND relkind IN ('r', 'v', 'S');
Это должно вывести соответствующие команды GRANT в GRANT SELECT для всех открытых таблиц, представлений и последовательностей для копирования-n-paste. Естественно, это будет применяться только к таблицам, которые уже были созданы.
Я думаю, что все решения можно улучшить и сделать метод более общим, если вы используете некоторые соглашения и рефлексию. Допустим, вы называете свои столбцы в таблице данных тем же именем, что и свойства в вашем объекте, тогда вы можете написать что-то, что будет проверять все ваши свойства вашего объекта, а затем искать этот столбец в таблице данных для сопоставления значения.
Я сделал противоположное, то есть ... от IList до datatable, и код, который я написал, можно увидеть по адресу: http://blog.tomasjansson.com/convert-datatable-to- generic-list-extension /
Не должно быть так сложно идти другим путем, и должно быть так сложно перегружать функции, чтобы вы могли предоставить информацию о том, какие свойства вы хотите включить или исключить.
РЕДАКТИРОВАТЬ: Таким образом, код, чтобы заставить его работать:
public static class DataTableExtensions
{
private static Dictionary<Type,IList<PropertyInfo>> typeDictionary = new Dictionary<Type, IList<PropertyInfo>>();
public static IList<PropertyInfo> GetPropertiesForType<T>()
{
var type = typeof(T);
if(!typeDictionary.ContainsKey(typeof(T)))
{
typeDictionary.Add(type, type.GetProperties().ToList());
}
return typeDictionary[type];
}
public static IList<T> ToList<T>(this DataTable table) where T : new()
{
IList<PropertyInfo> properties = GetPropertiesForType<T>();
IList<T> result = new List<T>();
foreach (var row in table.Rows)
{
var item = CreateItemFromRow<T>((DataRow)row, properties);
result.Add(item);
}
return result;
}
private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
{
T item = new T();
foreach (var property in properties)
{
property.SetValue(item, row[property.Name], null);
}
return item;
}
}
Если у вас есть DataTable, вы можете просто написать yourTable.ToList<YourType>()
и он создаст список для вас. Если у вас более сложный тип с вложенными объектами, вам нужно обновить код. Одно из предложений - просто перегрузить метод ToList
, чтобы принять params string[] excludeProperties
, который содержит все ваши свойства, которые не должны отображаться. Конечно, вы можете добавить проверку нуля в цикл foreach
метода CreateItemForRow
.
ОБНОВЛЕНИЕ: Добавлен статический словарь для сохранения результата от операции отражения, чтобы сделать его немного быстрее. Я не скомпилировал код, но он должен работать :).
Опаздывает, но это может быть полезно. Можно вызвать с помощью:
table.Map (); или вызвать Func для фильтрации значений.
Вы даже можете изменить имя отображения между свойством type и заголовком DataColumn, установив атрибуты для свойства.
[AttributeUsage(AttributeTargets.Property)]
public class SimppleMapperAttribute: Attribute
{
public string HeaderName { get; set; }
}
public static class SimpleMapper
{
#region properties
public static bool UseDeferredExecution { get; set; } = true;
#endregion
#region public_interface
public static IEnumerable<T> MapWhere<T>(this DataTable table, Func<T, bool> sortExpression) where T:new()
{
var result = table.Select().Select(row => ConvertRow<T>(row, table.Columns, typeof(T).GetProperties())).Where((t)=>sortExpression(t));
return UseDeferredExecution ? result : result.ToArray();
}
public static IEnumerable<T> Map<T>(this DataTable table) where T : new()
{
var result = table.Select().Select(row => ConvertRow<T>(row, table.Columns, typeof(T).GetProperties()));
return UseDeferredExecution ? result : result.ToArray();
}
#endregion
#region implementation_details
private static T ConvertRow<T>(DataRow row, DataColumnCollection columns, System.Reflection.PropertyInfo[] p_info) where T : new()
{
var instance = new T();
foreach (var info in p_info)
{
if (columns.Contains(GetMappingName(info))) SetProperty(row, instance, info);
}
return instance;
}
private static void SetProperty<T>(DataRow row, T instance, System.Reflection.PropertyInfo info) where T : new()
{
string mp_name = GetMappingName(info);
object value = row[mp_name];
info.SetValue(instance, value);
}
private static string GetMappingName(System.Reflection.PropertyInfo info)
{
SimppleMapperAttribute attribute = info.GetCustomAttributes(typeof(SimppleMapperAttribute),true).Select((o) => o as SimppleMapperAttribute).FirstOrDefault();
return attribute == null ? info.Name : attribute.HeaderName;
}
#endregion
}
Просто небольшое упрощение. Я не использую ItemArray:
List<Person> list = tbl.AsEnumerable().Select(x => new Person
{
Id = (Int32) (x["Id"]),
Name = (string) (x["Name"] ?? ""),
LastName = (string) (x["LastName"] ?? "")
}).ToList();