【问题标题】:Object Data Source with Reporting Services in Visual Studio在 Visual Studio 中使用 Reporting Services 的对象数据源
【发布时间】:2010-10-12 18:08:08
【问题描述】:

我正在一个网站上工作,我们希望在页面上包含一个饼图。现在,我正在通过带有对象数据源的 Reporting Services(RDLC 文件)来实现它。

我的对象数据源的SelectMethod是一个返回业务对象列表的方法;假设一个List<Alpha> 和Alpha 有一个带有Name 属性的子对象Beta。在我的报告定义中,我将类别组设置为: =Fields!Beta.Value.Name 这意味着 Alpha.Beta.Name 是我的饼图。我收到以下错误:

  • 报告处理过程中出现错误。分组“chart1_CategoryGroup1”的组表达式包含错误:对象变量或未设置块变量。

我能够确认这是因为 Beta 可以为空,并且能够通过更新对象 Alpha 以在 Beta 属性为空时返回新的 Beta() 来解决此问题。不过,这个解决方案并不理想,因为我的代码中还有其他地方需要 Beta 为空,如果它还没有值。

有没有办法更新报表定义以接受一个有效的空属性?理想情况下,如果 Beta 为空,我想将值指定为“未设置”。

【问题讨论】:

    标签: asp.net reporting-services objectdatasource


    【解决方案1】:

    我遇到了和你类似的问题,我使用 Null Object Refactoring 解决了它(非常感谢 Martin Fowler 的书 Refactoring :))。 在这里你可以找到很好的例子Null object refactoring

    因此,您可以创建继承 Beta 的类 NullableBeta,而属性 Name 和 e.g. IsNullable 在 Beta 实体上是虚拟的。

     public class Beta
        {
            public virtual Name{ get; set;}
            public virtual IsSet{ get{return true;}}
        }
        
        public class NullableBeta:Beta
        {
           public override Name
               { 
                 get{
                       return "Not Set";
                    }
                 set{}
              }
           public override IsSet{ get{ return false;}}
        }
    

    现在,如果没有在 Alfa 实体上设置 Beta,您可以返回 NullableBeta 实体的实例。在报告中,您将使用“未设置”而不是空字符串,并且在您将 Beta 实体设置为 Alfa 的地方,您可以检查 IsSet 属性而不是检查 Beta 是否为空。

    希望对你有帮助

    【讨论】:

    • 感谢您的帮助,但我搜索了一些不同的关键字,发现这个问题可以按照我想要的方式处理。你的可以,但我仍然需要更改我的代码来检查“IsSet”:stackoverflow.com/questions/1343283/…
    • 好的,你找到的解决方案确实比较简单。
    【解决方案2】:

    我遇到了和你类似的问题,我使用 Null Object Refactoring 解决了它(非常感谢 Martin Fowler 的书 Refactoring :))。在这里你可以找到很好的例子 Null 对象重构

    我首先通过实现Null object pattern 来解决 SSRS/RDLC 的这些缺点。 当涉及多于一两个领域对象时,手动实现这一点当然会费力。

    但是,由于我已经在使用 AutoMapper,@LucianBargaoanu 在 cmets 中正确指出,空对象作为 AutoMapper 的可选功能本机支持,因此不需要显式实现。

    因此,我使用 AutoMapper 及其 AllowNullDestinationValuesAllowNullCollectionsPreserveReferences()NullSubstituteForAllPropertyMaps() 功能,将我的所有域类映射为报告特定类并将所有空引用替换为空对象(将域对象空引用映射到报告对象时)或合理的默认值(例如空字符串的空字符串或Nullable<PrimitiveType> 的基础原始类型的默认值)。

    这里有一些示例代码来演示该方法:

    namespace Domain
    {
        public class MyClass
        {
            public int Id {get; set;}
            public string Name {get; set;} // could be null
            public string Code {get; set;} // could be null
            public decimal? SomeNullableValue {get; set;} // could be null
    
            public MyOtherClass OptionalOtherClass {get; set;} // could be null
        }
    
        public class MyOtherClass
        {
            public int OtherId {get; set;}
            public string OtherName {get; set;} // could be null
            public decimal? SomeOtherNullableValue {get; set;} // could be null
        }
    }
    
    namespace ReportViewModels
    {
        [Serializable]
        public class MyClass
        {
            public int Id {get; set;}
            public string Name {get; set;} // should not be null (but empty)
            public string Code {get; set;} // should not be null (but empty)
            public decimal? SomeNullableValue {get; set;} // should not be null (but default(decimal))
    
            public string CommonName
                => (Name + " " + Code).Trim();
    
            public MyOtherClass OptionalOtherClass {get; set;} // should not be null (but a MyOtherClass null object)
        }
    
        [Serializable]
        public class MyOtherClass
        {
            public int OtherId {get; set;}
            public string OtherName {get; set;} // should not be null (but empty)
            public decimal? SomeOtherNullableValue {get; set;} // should not be null (but default(decimal))
        }
    }
    
    public partial class Form1 : Form
    {
        private Context _context;
        private ReportObjectGenerator _reportObjectGenerator;
        
        public Form1(Context context, ReportObjectGenerator reportObjectGenerator)
        {
            _context = context;
            _reportObjectGenerator = reportObjectGenerator;
    
            InitializeComponent();
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
            var myDomainObjects = context.MyClass
                .Include(e => e.OptionalOtherClass)
                .ToList();
    
            var myReportViewModels = _reportObjectGenerator.GetReportObjects<Domain.MyClass, ReportViewModels.MyClass>(myDomainObjects);
    
            components ??= new System.ComponentModel.Container();
            
            //reportViewer1.LocalReport.ReportEmbeddedResource = "MyNamespace.Report1.rdlc";
            reportViewer1.LocalReport.ReportPath = "Report1.rdlc";
            reportViewer1.LocalReport.DataSources.Clear();
            reportViewer1.LocalReport.DataSources.Add(
                new ReportDataSource
                {
                    Name = "MyClassDataSet",
                    Value = new BindingSource(components)
                    {
                        DataMember = "MyClass",
                        DataSource = myReportViewModels
                    }
                });
            
            reportViewer1.RefreshReport();
        }
    }
    
    public class ReportObjectGenerator
    {
        public List<TDestination> GetReportObjects<TSource, TDestination>(
            IEnumerable<TSource> sourceObjects)
        {
            var domainNamespace = typeof(TSource).Namespace ?? throw new InvalidOperationException();
            var reportNamespace = typeof(TDestination).Namespace ?? throw new InvalidOperationException();
    
            var mapper = new MapperConfiguration(
                    cfg =>
                    {
                        cfg.AllowNullDestinationValues = false;
                        cfg.AllowNullCollections = false;
    
                        var allTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).ToList();
                        var allDomainTypes = allTypes.Where(t => t.Namespace?.StartsWith(domainNamespace) ?? false).ToList();
                        var allReportTypes = allTypes.Where(t => t.Namespace?.StartsWith(reportNamespace) ?? false).ToList();
    
                        foreach (var reportClassType in allReportTypes)
                        {
                            var domainClassType = allDomainTypes.Single(t => t.Name == reportClassType.Name);
    
                            cfg.CreateMap(domainClassType, reportClassType)
                                .PreserveReferences();
                        }
    
                        // If we want to set the default value of the underlying type of Nullable<UnderlyingType>
                        // properties in case they would be null, than AllowNullDestinationValues is not enough and we
                        // need to manually replace the null value here.
                        cfg.ForAllPropertyMaps(
                            pm => pm.SourceMember.GetMemberType().IsNullableType(),
                            (p, _) => p.NullSubstitute ??= Activator.CreateInstance(p.SourceMember.GetMemberType().GetTypeOfNullable()));
                    })
                .CreateMapper();
    
            return mapper.Map<IEnumerable<TSource>, List<TDestination>>(sourceObjects);
        }
    }
    

    【讨论】:

    • AM内置默认值,研究AllowNullDestinationValuesAllowNullCollections
    • @LucianBargaoanu 好电话!仍然需要一些代码来确保 Nullable&lt;T&gt; 属性的默认值,但现在整体代码更加紧凑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-05
    • 2015-07-29
    • 1970-01-01
    • 2012-11-22
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多